doc: add quick intro to autoloading
This commit is contained in:
parent
89d63b2523
commit
a7498ef50d
|
@ -15,3 +15,195 @@ 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(&$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 \Frienidca;
|
||||
|
||||
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 \Frienidca;
|
||||
|
||||
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 ("\").
|
||||
|
||||
|
|
Loading…
Reference in a new issue