209 lines
6.5 KiB
Markdown
209 lines
6.5 KiB
Markdown
Autoloader
|
|
==========
|
|
|
|
* [Home](help)
|
|
|
|
There is some initial support to class autoloading in Friendica core.
|
|
|
|
The autoloader code is in `include/autoloader.php`.
|
|
It's derived from composer autoloader code.
|
|
|
|
Namespaces and Classes are mapped to folders and files in `library/`,
|
|
and the map must be updated by hand, because we don't use composer yet.
|
|
The mapping is defined by files in `include/autoloader/` folder.
|
|
|
|
Currently, only HTMLPurifier library is loaded using autoloader.
|
|
|
|
|
|
## A quick introdution to class autoloading
|
|
|
|
The autoloader it's a way for php to automagically include the file that define a class when the class is first used, without the need to use "require_once" every time.
|
|
|
|
Once is setup you don't have to use it in any way. You need a class? you use the class.
|
|
|
|
At his basic is a function passed to the "spl_autoload_register()" function, which receive as argument the class name the script want and is it job to include the correct php file where that class is defined.
|
|
The best source for documentation is [php site](http://php.net/manual/en/language.oop5.autoload.php).
|
|
|
|
One example, based on fictional friendica code.
|
|
|
|
Let's say you have a php file in "include/" that define a very useful class:
|
|
|
|
```
|
|
file: include/ItemsManager.php
|
|
<?php
|
|
namespace \Friendica;
|
|
|
|
class ItemsManager {
|
|
public function getAll() { ... }
|
|
public function getByID($id) { ... }
|
|
}
|
|
```
|
|
|
|
The class "ItemsManager" has been declared in "Friendica" namespace.
|
|
Namespaces are useful to keep things separated and avoid names clash (could be that a library you want to use defines a class named "ItemsManager", but as long as is in another namespace, you don't have any problem)
|
|
|
|
If we were using composer, we had configured it with path where to find the classes of "Friendica" namespace, and then the composer script will generate the autoloader machinery for us.
|
|
As we don't use composer, we need check that the autoloader knows the Friendica namespace.
|
|
So in "include/autoloader/autoload_psr4.php" there should be something like
|
|
|
|
```
|
|
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
|
$baseDir = dirname($vendorDir);
|
|
return array(
|
|
"Friendica" => array($baseDir."/include");
|
|
);
|
|
```
|
|
|
|
|
|
That tells the autoloader code to look for files that defines classes in "Friendica" namespace under "include/" folder. (And btw, that's why the file has the same name as the class it defines.)
|
|
|
|
*note*: The structure of files in "include/autoloader/" has been copied from the code generated by composer, to ease the work of enable autoloader for external libraries under "library/"
|
|
|
|
Let's say now that you need to load some items in a view, maybe in a fictional "mod/network.php".
|
|
Somewere at the start of the scripts, the autoloader was initialized. In Friendica is done at the top of "boot.php", with "require_once('include/autoloader.php');".
|
|
|
|
The code will be something like:
|
|
|
|
```
|
|
file: mod/network.php
|
|
<?php
|
|
|
|
function network_content(App $a) {
|
|
$itemsmanager = new \Friendica\ItemsManager();
|
|
$items = $itemsmanager->getAll();
|
|
|
|
// pass $items to template
|
|
// return result
|
|
}
|
|
```
|
|
|
|
That's a quite simple example, but look: no "require()"!
|
|
You need to use a class, you use the class and you don't need to do anything more.
|
|
|
|
Going further: now we have a bunch of "*Manager" classes that cause some code duplication, let's define a BaseManager class, where to move all code in common between all managers:
|
|
|
|
```
|
|
file: include/BaseManager.php
|
|
<?php
|
|
namespace \Friendica;
|
|
|
|
class BaseManager {
|
|
public function thatFunctionEveryManagerUses() { ... }
|
|
}
|
|
```
|
|
|
|
and then let's change the ItemsManager class to use this code
|
|
|
|
```
|
|
file: include/ItemsManager.php
|
|
<?php
|
|
namespace \Friendica;
|
|
|
|
class ItemsManager extends BaseManager {
|
|
public function getAll() { ... }
|
|
public function getByID($id) { ... }
|
|
}
|
|
```
|
|
|
|
The autoloader don't mind what you need the class for. You need a class, you get the class.
|
|
It works with the "BaseManager" example here, it works when we need to call static methods on a class:
|
|
|
|
```
|
|
file: include/dfrn.php
|
|
<?php
|
|
namespace \Friendica;
|
|
|
|
class dfrn {
|
|
public static function mail($item, $owner) { ... }
|
|
}
|
|
```
|
|
|
|
```
|
|
file: mod/mail.php
|
|
<?php
|
|
|
|
mail_post($a){
|
|
...
|
|
\Friendica\dfrn::mail($item, $owner);
|
|
...
|
|
}
|
|
```
|
|
|
|
If your code is in same namespace as the class you need, you don't need to prepend it:
|
|
|
|
```
|
|
file: include/delivery.php
|
|
<?php
|
|
|
|
namespace \Friendica;
|
|
|
|
// this is the same content of current include/delivery.php,
|
|
// but has been declared to be in "Friendica" namespace
|
|
|
|
[...]
|
|
switch($contact['network']) {
|
|
|
|
case NETWORK_DFRN:
|
|
if ($mail) {
|
|
$item['body'] = ...
|
|
$atom = dfrn::mail($item, $owner);
|
|
} elseif ($fsuggest) {
|
|
$atom = dfrn::fsuggest($item, $owner);
|
|
q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
|
|
} elseif ($relocate)
|
|
$atom = dfrn::relocate($owner, $uid);
|
|
[...]
|
|
```
|
|
|
|
This is real "include/delivery.php" unchanged, but as the code is declared to be in "Friendica" namespace, you don't need to write it when you need to use the "dfrn" class.
|
|
But if you want to use classes from another library, you need to use the full namespace, e.g.
|
|
|
|
```
|
|
<?php
|
|
namespace \Friendica;
|
|
|
|
class Diaspora {
|
|
public function md2bbcode() {
|
|
$html = \Michelf\MarkdownExtra::defaultTransform($text);
|
|
}
|
|
}
|
|
```
|
|
|
|
if you use that class in many places of the code and you don't want to write the full path to the class everytime, you can use the "use" php keyword
|
|
|
|
```
|
|
<?php
|
|
namespace \Friendica;
|
|
|
|
use \Michelf\MarkdownExtra;
|
|
|
|
class Diaspora {
|
|
public function md2bbcode() {
|
|
$html = MarkdownExtra::defaultTransform($text);
|
|
}
|
|
}
|
|
```
|
|
|
|
Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.
|
|
You can go more deep if you want to, like:
|
|
|
|
```
|
|
<?php
|
|
namespace \Friendica\Network;
|
|
|
|
class DFRN {
|
|
}
|
|
```
|
|
|
|
or
|
|
|
|
```
|
|
<?php
|
|
namespace \Friendica\DBA;
|
|
|
|
class MySQL {
|
|
}
|
|
```
|
|
|
|
So you can think of namespaces as folders in a unix filesystem, with global scope as the root ("\").
|
|
|