]> git.mxchange.org Git - friendica.git/commitdiff
doc: add quick intro to autoloading
authorfabrixxm <fabrix.xm@gmail.com>
Sat, 13 Feb 2016 09:57:37 +0000 (10:57 +0100)
committerfabrixxm <fabrix.xm@gmail.com>
Sat, 13 Feb 2016 09:57:37 +0000 (10:57 +0100)
doc/autoloader.md

index ea1a82b3572dbbfa4f84a2dc62e046b7fdb7abc0..947eade23cfa62701ccd2c08051b95fb29419185 100644 (file)
@@ -15,3 +15,195 @@ The mapping is defined by files in `include/autoloader/` folder.
 Currently, only HTMLPurifier library is loaded using autoloader.\r
 \r
 \r
+## A quick introdution to class autoloading\r
+\r
+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.\r
+\r
+Once is setup you don't have to use it in any way. You need a class? you use the class.\r
+\r
+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.\r
+The best source for documentation is [php site](http://php.net/manual/en/language.oop5.autoload.php).\r
+\r
+One example, based on fictional friendica code.\r
+\r
+Let's say you have a php file in "include/" that define a very useful class:\r
+\r
+```\r
+    file: include/ItemsManager.php\r
+    <?php\r
+    namespace \Friendica;\r
+    \r
+    class ItemsManager {\r
+       public function getAll() { ... }\r
+       public function getByID($id) { ... }\r
+    }\r
+```\r
+\r
+The class "ItemsManager" has been declared in "Friendica" namespace.\r
+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)\r
+\r
+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.\r
+As we don't use composer, we need check that the autoloader knows the Friendica namespace.\r
+So in "include/autoloader/autoload_psr4.php" there should be something like\r
+\r
+```\r
+    $vendorDir = dirname(dirname(dirname(__FILE__)))."/library";\r
+    $baseDir = dirname($vendorDir);\r
+    return array(\r
+       "Friendica" => array($baseDir."/include");\r
+    );\r
+```\r
+\r
+\r
+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.)\r
+\r
+*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/"\r
+\r
+Let's say now that you need to load some items in a view, maybe in a fictional "mod/network.php".\r
+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');".\r
+\r
+The code will be something like:\r
+\r
+```\r
+    file: mod/network.php\r
+    <?php\r
+    \r
+    function network_content(&$a) {\r
+       $itemsmanager = new \Friendica\ItemsManager();\r
+       $items = $itemsmanager->getAll();\r
+    \r
+       // pass $items to template\r
+       // return result\r
+    }\r
+```\r
+\r
+That's a quite simple example, but look: no "require()"!\r
+You need to use a class, you use the class and you don't need to do anything more.\r
+\r
+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:\r
+\r
+```\r
+    file: include/BaseManager.php\r
+    <?php\r
+    namespace \Friendica;\r
+    \r
+    class BaseManager {\r
+      public function thatFunctionEveryManagerUses() { ... }\r
+    }\r
+```\r
+\r
+and then let's change the ItemsManager class to use this code\r
+\r
+```\r
+    file: include/ItemsManager.php\r
+    <?php\r
+    namespace \Friendica;\r
+    \r
+    class ItemsManager extends BaseManager {\r
+       public function getAll() { ... }\r
+       public function getByID($id) { ... }\r
+    }\r
+```\r
+\r
+The autoloader don't mind what you need the class for. You need a class, you get the class.\r
+It works with the "BaseManager" example here, it works when we need to call static methods on a class:\r
+\r
+```\r
+    file: include/dfrn.php\r
+    <?php    \r
+    namespace \Friendica;\r
+    \r
+    class dfrn {\r
+      public static function  mail($item, $owner) { ... }\r
+    }\r
+```\r
+\r
+```\r
+    file: mod/mail.php\r
+    <?php\r
+    \r
+    mail_post($a){\r
+     ...\r
+     \Friendica\dfrn::mail($item, $owner);\r
+     ...\r
+    }\r
+```\r
+\r
+If your code is in same namespace as the class you need, you don't need to prepend it:\r
+\r
+```\r
+    file: include/delivery.php\r
+    <?php\r
+    \r
+    namespace \Friendica;\r
+    \r
+    // this is the same content of current include/delivery.php, \r
+    // but has been declared to be in "Friendica" namespace\r
+    \r
+    [...]\r
+    switch($contact['network']) {\r
+    \r
+        case NETWORK_DFRN:\r
+            if ($mail) {\r
+                $item['body'] = ...\r
+                $atom = dfrn::mail($item, $owner);\r
+            } elseif ($fsuggest) {\r
+                $atom = dfrn::fsuggest($item, $owner);\r
+                q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));\r
+            } elseif ($relocate)\r
+                $atom = dfrn::relocate($owner, $uid);\r
+    [...]\r
+```\r
+\r
+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.\r
+But if you want to use classes from another library, you need to use the full namespace, e.g.\r
+\r
+```\r
+    <?php\r
+    namespace \Frienidca;\r
+    \r
+    class Diaspora {\r
+      public function md2bbcode() {\r
+        $html = \Michelf\MarkdownExtra::defaultTransform($text); \r
+      }\r
+    }\r
+```\r
+\r
+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\r
+\r
+```\r
+    <?php\r
+    namespace \Frienidca;\r
+    \r
+    use \Michelf\MarkdownExtra;\r
+    \r
+    class Diaspora {\r
+      public function md2bbcode() {\r
+        $html = MarkdownExtra::defaultTransform($text); \r
+      }\r
+    }\r
+```\r
+\r
+Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.\r
+You can go more deep if you want to, like:\r
+\r
+```\r
+    <?php\r
+    namespace \Friendica\Network;\r
+    \r
+    class DFRN {\r
+    }\r
+```\r
+\r
+or\r
+\r
+```\r
+    <?php\r
+    namespace \Friendica\DBA;\r
+    \r
+    class MySQL {\r
+    }\r
+```\r
+\r
+So you can think of namespaces as folders in a unix filesystem, with global scope as the root ("\").\r
+\r