I just started to learn laravel. After reading some official documents in a day, I started learning laravel. What is talked about here are the most basic things, and all the experts can just skip it.
composer overview
At the beginning, what attracted me the most was Composer, because I had never used Composer before.
Composer is a tool used in PHP to manage dependencies. You only need to declare the external tool libraries you depend on in your project, and Composer will help you install these dependent library files. Running Composer requires PHP 5.3.2+ or above.
Use composer
The first step is to declare dependencies. Let's say you're creating a project that needs a library for logging. You decide to use monolog. In order to add it to your project, all you need to do is create a composer.json
file that describes the project's dependencies.
<span>{ </span><span>"</span><span>require</span><span>"</span><span>: { </span><span>"</span><span>monolog/monolog</span><span>"</span>: <span>"</span><span>1.2.*</span><span>"</span><span> } }</span>
The second step is to use composer. In the project root directory, execute the installation command. After execution, monolog will be downloaded to the vendor/monolog/monolog
directory.
$ php composer.phar install
The third step is automatic loading of classes. In addition to library download, Composer also prepares an automatic loading file, which can load all class files in the library downloaded by Composer. To use it, you just need to add the following line of code to your project's bootstrap file:
require <span>'</span><span>vendor/autoload.php</span><span>'</span>;
This makes it easy for you to use third-party code. For example: if your project depends on monolog, you can start using the library like this and they will be automatically loaded.
<span>$log</span> = <span>new</span> Monolog\Logger('name'<span>); </span><span>$log</span>->pushHandler(<span>new</span> Monolog\Handler\StreamHandler('app.log', Monolog\Logger::<span>WARNING)); </span><span>$log</span>->addWarning('Foo');
Composer auto-loading exploration
When using tools in the real world, if you understand how the tool works, you will be more confident in using it. For a novice who is new to Laravel and Composer for the first time, he will be more comfortable using it if he understands how Composer works.
My understanding is that composer downloads code files from the sources of relevant libraries based on the declared dependencies, and generates PHP scripts for automatic class loading in the Composer directory based on the dependencies. When used, introduce " /vendor/autoload.php” file, you can directly instantiate classes in these third-party libraries. So, how does Composer implement automatic loading of classes? Next, let’s start from laravel’s entry file and follow it up to see the mystery of Composer’s automatic loading.
1. Code List laravel/public/index.php
<span>#laravel/public/index.php<br /><br />require</span> __DIR__.'/../bootstrap/autoload.php'<span>; </span><span>$app</span> = <span>require_once</span> __DIR__.'/../bootstrap/start.php'<span>; </span><span>$app</span>->run();
The first line introduces laravel/bootstrap/autoload.php without explanation, open the file .
2. Code List laravel/bootstrap/autoload.php
<span>define</span>('LARAVEL_START', <span>microtime</span>(<span>true</span><span>)); </span><span>require</span> __DIR__.'/../vendor/autoload.php'<span>; </span><span>if</span> (<span>file_exists</span>(<span>$compiled</span> = __DIR__.'/compiled.php'<span>)) { </span><span>require</span> <span>$compiled</span><span>; } Patchwork\Utf8\Bootup</span>::<span>initMbstring();<br /></span>
The first line defines the time point when the program starts execution. Immediately following the second line, laravel/vendor/autoload.php is introduced.
The seventh line, as mentioned before, after introducing Composer's autoload.php, you can directly use the classes in the third-party class library. Here is the directly used Bootup class. Let’s take a look at what /vendor/autoload.php does.
3. Code List laravel/vendor/autoload.php
<span>1</span> <span>//</span><span> autoload.php @generated by Composer</span> <span>2</span> <span>3</span> <span>require_once</span> __DIR__ . '/composer' . '/autoload_real.php'<span>; </span><span>4</span> <span>5</span> <span>return</span> ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256::getLoader();
When you get here, you will immediately enter the automatically added door.
This file is very simple. Are you confused by the function name on line 5? Don't be scared, it's just a class name. This class is declared in the file laravel/vendor/composer/autoload_real.php introduced on line 3. Next, open the file and see getLoader();
4. Code Listlaravel/vendor/composer/autoload_real.php
<span> 1</span> <?<span>php </span><span> 2</span> <span> 3</span> <span>//</span><span> autoload_real.php @generated by Composer</span> <span> 4</span> <span> 5</span> <span>class</span><span> ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256 </span><span> 6</span> <span>{ </span><span> 7</span> <span>private</span> <span>static</span> <span>$loader</span><span>; </span><span> 8</span> <span> 9</span> <span>public</span> <span>static</span> <span>function</span> loadClassLoader(<span>$class</span><span>) </span><span>10</span> <span> { </span><span>11</span> <span>if</span> ('Composer\Autoload\ClassLoader' === <span>$class</span><span>) { </span><span>12</span> <span>require</span> __DIR__ . '/ClassLoader.php'<span>; </span><span>13</span> <span> } </span><span>14</span> <span> } </span><span>15</span> <span>16</span> <span>17</span> <span>public</span> <span>static</span> <span>function</span><span> getLoader() </span><span>18</span> <span> { </span><span>19</span> <span>if</span> (<span>null</span> !== self::<span>$loader</span><span>) { </span><span>20</span> <span>return</span> self::<span>$loader</span><span>; </span><span>21</span> <span> } </span><span>22</span> <span>23</span> spl_autoload_register(<span>array</span>('ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256', 'loadClassLoader'), <span>true</span>, <span>true</span><span>); </span><span>24</span> self::<span>$loader</span> = <span>$loader</span> = <span>new</span><span> \Composer\Autoload\ClassLoader(); </span><span>25</span> spl_autoload_unregister(<span>array</span>('ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256', 'loadClassLoader'<span>)); </span><span>26</span> <span>27</span> <span>$vendorDir</span> = <span>dirname</span><span>(__DIR__); </span><span>28</span> <span>$baseDir</span> = <span>dirname</span>(<span>$vendorDir</span><span>); </span><span>29</span> <span>30</span> <span>$includePaths</span> = <span>require</span> __DIR__ . '/include_paths.php'<span>; </span><span>31</span> <span>32</span> <span>array_push</span>(<span>$includePaths</span>, <span>get_include_path</span><span>()); </span><span>33</span> <span>set_include_path</span>(<span>join</span>(PATH_SEPARATOR, <span>$includePaths</span><span>)); </span><span>34</span> <span>35</span> <span>36</span> <span>$map</span> = <span>require</span> __DIR__ . '/autoload_namespaces.php'<span>; </span><span>37</span> <span>foreach</span> (<span>$map</span> <span>as</span> <span>$namespace</span> => <span>$path</span><span>) { </span><span>38</span> <span>$loader</span>->set(<span>$namespace</span>, <span>$path</span><span>); </span><span>39</span> <span> } </span><span>40</span> <span>41</span> <span>$map</span> = <span>require</span> __DIR__ . '/autoload_psr4.php'<span>; </span><span>42</span> <span>foreach</span> (<span>$map</span> <span>as</span> <span>$namespace</span> => <span>$path</span><span>) { </span><span>43</span> <span>$loader</span>->setPsr4(<span>$namespace</span>, <span>$path</span><span>); </span><span>44</span> <span> } </span><span>45</span> <span>46</span> <span>$classMap</span> = <span>require</span> __DIR__ . '/autoload_classmap.php'<span>; </span><span>47</span> <span>if</span> (<span>$classMap</span><span>) { </span><span>48</span> <span>$loader</span>->addClassMap(<span>$classMap</span><span>); </span><span>49</span> <span> } </span><span>50</span> <span>51</span> <span>52</span> <span>$loader</span>->register(<span>true</span><span>); </span><span>53</span> <span>54</span> <span>$includeFiles</span> = <span>require</span> __DIR__ . '/autoload_files.php'<span>; </span><span>55</span> <span>foreach</span> (<span>$includeFiles</span> <span>as</span> <span>$file</span><span>) { </span><span>56</span> composerRequire9b2a1b1cf01c9a870ab98748dc5f1256(<span>$file</span><span>); </span><span>57</span> <span> } </span><span>58</span> <span>59</span> <span>return</span> <span>$loader</span><span>; </span><span>60</span> <span> } </span><span>61</span> <span>} </span><span>62</span> <span>63</span> <span>function</span> composerRequire9b2a1b1cf01c9a870ab98748dc5f1256(<span>$file</span><span>)及 $loader->addClassMap() </span><span>64</span> <span>{ </span><span>65</span> <span>require</span> <span>$file</span><span>; </span><span>66</span> }
In line 17, getLoader() first determines the $loader value in the current class, and returns it if it is not null. This can be skipped. Then instantiate the ClassLoader class to $loader, laravel/vendor/composer/ClassLoader.php
Several files are introduced here. These files are automatically generated by composer. When dependencies change, there is no need to modify these scripts, just run composer to regenerate them.
laravel/vendor/composer/autoloade_namespace.php
laravel/vendor/composer/autoloade_prs4.php
laravel/vendor/composer/autoloade_classmap.php
laravel/vendor/composer/autoloade_files.php
After setting a bunch of path information, executed $loader->set(), $loader->setPsr4() and $loader->addClassMap(), and then executed $loader-> register(true); Now let’s look at them one by one.
5. Code Listlaravel/vendor/composer/ClassLoader.php
1 php 2 3 /* 4 * This file is part of Composer. 5 * 6 * (c) Nils Adermann$loader->set($namespace, $path);Psr0标准
设置命名空间对应的路径,以便于随后自动加载相关类文件。
$loader->setPsr4($namespace, $path);Psr4标准
设置命名空间对应的路径,以便于随后自动加载相关类文件。
$loader->addClassMap($classMap);
设置类文件路径与类名的对应关系,以便于随后自动加载相关类文件。
$loader->register(true);
<span>public</span> <span>function</span> register(<span>$prepend</span> = <span>false</span><span>) { spl_autoload_register(</span><span>array</span>(<span>$this</span>, 'loadClass'), <span>true</span>, <span>$prepend</span><span>); }</span>
这里设置了 欲注册的自动装载函数 $this->loadClass(),关于 spl_autoload_register 和 spl_autoload_unregister 的更多信息随后会有专门的解释。现在打开loadClass()的定义
<span>public</span> <span>function</span> loadClass(<span>$class</span><span>) { </span><span>if</span> (<span>$file</span> = <span>$this</span>->findFile(<span>$class</span><span>)) { includeFile(</span><span>$file</span><span>); </span><span>return</span> <span>true</span><span>; } }</span>
这里有个 findFile() 函数,如果相关类的声明所在文件的路径找到了,就包含并运行该文件,然后返回 true 。接着打开findFile()的定义
<span>public</span> <span>function</span> findFile(<span>$class</span><span>) { </span><span>//</span><span> work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731</span> <span>if</span> ('\\' == <span>$class</span>[0<span>]) { </span><span>$class</span> = <span>substr</span>(<span>$class</span>, 1<span>); } </span><span>//</span><span> class map lookup</span> <span>if</span> (<span>isset</span>(<span>$this</span>->classMap[<span>$class</span><span>])) { </span><span>return</span> <span>$this</span>->classMap[<span>$class</span><span>]; } </span><span>$file</span> = <span>$this</span>->findFileWithExtension(<span>$class</span>, '.php'<span>); </span><span>//</span><span> Search for Hack files if we are running on HHVM</span> <span>if</span> (<span>$file</span> === <span>null</span> && <span>defined</span>('HHVM_VERSION'<span>)) { </span><span>$file</span> = <span>$this</span>->findFileWithExtension(<span>$class</span>, '.hh'<span>); } </span><span>if</span> (<span>$file</span> === <span>null</span><span>) { </span><span>//</span><span> Remember that this class does not exist.</span> <span>return</span> <span>$this</span>->classMap[<span>$class</span>] = <span>false</span><span>; } </span><span>return</span> <span>$file</span><span>; }</span>
先是判断类名是否以'\'开始,如果是的话,清除开头的'\'
接着,检查当前类的名字是否在 类名与声明当前类的文件的路径的关系数组 中,如果存在,直接返回相关键值(类文件路径信息)
如果上一步没有返回路径信息,执行 findFileWithExtension($class, '.php') 继续查找类文件路径信息,findFileWithExtension的定义后面将会列出。
如果仍未找到类的文件路径信息,返回值为 null 且定义了 HHVM_VERSION 信息,则用“.hh”后缀继续查找类文件信息。
HHVM_VERSION 是 HHVM版本信息? HHVM 是 Facebook 开发的高性能 PHP 虚拟机,宣称比官方的快9倍。
如果仍未找到,则返回 false 。Remember that this class does not exist.(这句注释很有喜感?哈哈)
代码清单 findFileWithExtension()
<span> 1</span> <span>private</span> <span>function</span> findFileWithExtension(<span>$class</span>, <span>$ext</span><span>) </span><span> 2</span> <span>{ </span><span> 3</span> <span>//</span><span> PSR-4 lookup</span> <span> 4</span> <span>$logicalPathPsr4</span> = <span>strtr</span>(<span>$class</span>, '\\', DIRECTORY_SEPARATOR) . <span>$ext</span><span>; </span><span> 5</span> <span> 6</span> <span>$first</span> = <span>$class</span>[0<span>]; </span><span> 7</span> <span>if</span> (<span>isset</span>(<span>$this</span>->prefixLengthsPsr4[<span>$first</span><span>])) { </span><span> 8</span> <span>foreach</span> (<span>$this</span>->prefixLengthsPsr4[<span>$first</span>] <span>as</span> <span>$prefix</span> => <span>$length</span><span>) { </span><span> 9</span> <span>if</span> (0 === <span>strpos</span>(<span>$class</span>, <span>$prefix</span><span>)) { </span><span>10</span> <span>foreach</span> (<span>$this</span>->prefixDirsPsr4[<span>$prefix</span>] <span>as</span> <span>$dir</span><span>) { </span><span>11</span> <span>if</span> (<span>file_exists</span>(<span>$file</span> = <span>$dir</span> . DIRECTORY_SEPARATOR . <span>substr</span>(<span>$logicalPathPsr4</span>, <span>$length</span><span>))) { </span><span>12</span> <span>return</span> <span>$file</span><span>; </span><span>13</span> <span> } </span><span>14</span> <span> } </span><span>15</span> <span> } </span><span>16</span> <span> } </span><span>17</span> <span> } </span><span>18</span> <span>19</span> <span>//</span><span> PSR-4 fallback dirs</span> <span>20</span> <span>foreach</span> (<span>$this</span>->fallbackDirsPsr4 <span>as</span> <span>$dir</span><span>) { </span><span>21</span> <span>if</span> (<span>file_exists</span>(<span>$file</span> = <span>$dir</span> . DIRECTORY_SEPARATOR . <span>$logicalPathPsr4</span><span>)) { </span><span>22</span> <span>return</span> <span>$file</span><span>; </span><span>23</span> <span> } </span><span>24</span> <span> } </span><span>25</span> <span>26</span> <span>//</span><span> PSR-0 lookup</span> <span>27</span> <span>if</span> (<span>false</span> !== <span>$pos</span> = <span>strrpos</span>(<span>$class</span>, '\\'<span>)) { </span><span>28</span> <span>//</span><span> namespaced class name</span> <span>29</span> <span>$logicalPathPsr0</span> = <span>substr</span>(<span>$logicalPathPsr4</span>, 0, <span>$pos</span> + 1<span>) </span><span>30</span> . <span>strtr</span>(<span>substr</span>(<span>$logicalPathPsr4</span>, <span>$pos</span> + 1), '_',<span> DIRECTORY_SEPARATOR); </span><span>31</span> } <span>else</span><span> { </span><span>32</span> <span>//</span><span> PEAR-like class name</span> <span>33</span> <span>$logicalPathPsr0</span> = <span>strtr</span>(<span>$class</span>, '_', DIRECTORY_SEPARATOR) . <span>$ext</span><span>; </span><span>34</span> <span> } </span><span>35</span> <span>36</span> <span>if</span> (<span>isset</span>(<span>$this</span>->prefixesPsr0[<span>$first</span><span>])) { </span><span>37</span> <span>foreach</span> (<span>$this</span>->prefixesPsr0[<span>$first</span>] <span>as</span> <span>$prefix</span> => <span>$dirs</span><span>) { </span><span>38</span> <span>if</span> (0 === <span>strpos</span>(<span>$class</span>, <span>$prefix</span><span>)) { </span><span>39</span> <span>foreach</span> (<span>$dirs</span> <span>as</span> <span>$dir</span><span>) { </span><span>40</span> <span>if</span> (<span>file_exists</span>(<span>$file</span> = <span>$dir</span> . DIRECTORY_SEPARATOR . <span>$logicalPathPsr0</span><span>)) { </span><span>41</span> <span>return</span> <span>$file</span><span>; </span><span>42</span> <span> } </span><span>43</span> <span> } </span><span>44</span> <span> } </span><span>45</span> <span> } </span><span>46</span> <span> } </span><span>47</span> <span>48</span> <span>//</span><span> PSR-0 fallback dirs</span> <span>49</span> <span>foreach</span> (<span>$this</span>->fallbackDirsPsr0 <span>as</span> <span>$dir</span><span>) { </span><span>50</span> <span>if</span> (<span>file_exists</span>(<span>$file</span> = <span>$dir</span> . DIRECTORY_SEPARATOR . <span>$logicalPathPsr0</span><span>)) { </span><span>51</span> <span>return</span> <span>$file</span><span>; </span><span>52</span> <span> } </span><span>53</span> <span> } </span><span>54</span> <span>55</span> <span>//</span><span> PSR-0 include paths.</span> <span>56</span> <span>if</span> (<span>$this</span>->useIncludePath && <span>$file</span> = stream_resolve_include_path(<span>$logicalPathPsr0</span><span>)) { </span><span>57</span> <span>return</span> <span>$file</span><span>; </span><span>58</span> <span> } </span><span>59</span> }
该函数唯一的目的就是根据刚才通过$loader->set($namespace, $path)和$loader->setPsr4($namespace, $path)方法设置的信息找出类文件的路径信息。
接下来,我们回到代码清单laravel/vendor/composer/autoload_real.php ,为精简篇幅,这里只贴出片段(续上节的 $loader->register(true))。
<span>$loader</span>->register(<span>true</span><span>); </span><span>$includeFiles</span> = <span>require</span> __DIR__ . '/autoload_files.php'<span>; </span><span>foreach</span> (<span>$includeFiles</span> <span>as</span> <span>$file</span><span>) { composerRequire9b2a1b1cf01c9a870ab98748dc5f1256(</span><span>$file</span><span>); } </span><span>return</span> <span>$loader</span><span>; } } </span><span>function</span> composerRequire9b2a1b1cf01c9a870ab98748dc5f1256(<span>$file</span><span>) { </span><span>require</span> <span>$file</span><span>; }</span>
在经历了一番长途跋涉后,终于从 laravel/vendor/composer/ClassLoader.php 中出来了。继 $loader->register(true) 之后,又引入了laravel/vendor/composer/autoload_files.php,相比之下,这个文件要简单得多,只是个数组,列出了几个文件路径。
<span>//</span><span> autoload_files.php @generated by Composer</span> <span>$vendorDir</span> = <span>dirname</span>(<span>dirname</span>(<span>__FILE__</span><span>)); </span><span>$baseDir</span> = <span>dirname</span>(<span>$vendorDir</span><span>); </span><span>return</span> <span>array</span><span>( </span><span>$vendorDir</span> . '/ircmaxell/password-compat/lib/password.php', <span>$vendorDir</span> . '/swiftmailer/swiftmailer/lib/swift_required.php', <span>$vendorDir</span> . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php', <span>$vendorDir</span> . '/laravel/framework/src/Illuminate/Support/helpers.php',<span> );</span>
接着就是用 composerRequire9b2a1b1cf01c9a870ab98748dc5f1256() 函数 包含并运行 这几个文件。这四个文件的具体信息,随后会专门写博文来认识。
最后 , 返回 ClassLoader 类的实例 $loader 。
现在在回到 代码清单 laravel/bootstrap/autoload.php 的第7行
<span>1</span> <span>define</span>('LARAVEL_START', <span>microtime</span>(<span>true</span><span>)); </span><span>2</span> <span>require</span> __DIR__.'/../vendor/autoload.php'<span>; </span><span>3</span> <span>if</span> (<span>file_exists</span>(<span>$compiled</span> = __DIR__.'/compiled.php'<span>)) </span><span>4</span> <span>{ </span><span>5</span> <span>require</span> <span>$compiled</span><span>; </span><span>6</span> <span>} </span><span>7</span> Patchwork\Utf8\Bootup::initMbstring();
注意这里第7行,之所以可以直接像 Patchwork\Utf8\Bootup::initMbstring() 这么使用而不需要手动required Bootup类文件,是因为 前面在ClassLoader中的register() 函数用 spl_autoload_register() 对Bootup类进行了注册。下面说一下 spl_autoload_register 。
spl_autoload_register
要使用 spl_autoload_register ,请保证你的PHP版本(PHP 5 >= 5.1.2)。
www.php.net 对 spl_autoload_register 的解释如下:注册__autoload()函数,将函数注册到SPL __autoload函数栈中。如果该栈中的函数尚未激活,则激活它们。刚接触 PHP 的同学肯定觉得这个解释云里雾里的,看完依然不知道什么意思。要想理解这句话,首先要弄明白 __autoload() 是个什么东西。
__autoload()
__autoload 的作用是尝试加载未定义的类,可以通过定义这个函数来启用类的自动加载。下面举个例子:
<span>function</span> __autoload(<span>$class</span><span>) { </span><span>echo</span> '尝试加载的类的名字是:'.<span>$class</span><span>; } </span><span>$say</span>= @ <span>new</span> say();
上例会输出:"尝试加载的类的名字是 say "。由于最后一行引用了尚未定义的类 box ,所以 __autoload 函数将被执行。
再看下面这段
class say { public function __construct() { echo 'say 类存在,并说出了hello,所以 __autoload 函数不会执行。'; } } <span>function</span> __autoload(<span>$class</span><span>) { </span><span>echo</span> '尝试加载的类的名字是:'.<span>$class</span><span>; } </span><span>$say</span>= @ <span>new</span> say();
这将会输出 : say 类存在,并说出了hello,所以 __autoload 函数不会执行。
理解完 __autoload 就好办了,再看上面:“将函数注册到SPL __autoload函数栈中”,意思是我们可以自定义 尝试加载未定义的类时 使用的函数。现在返回代码片段
spl_autoload_register(<span>array</span>(<span>$this</span>, 'loadClass'), <span>true</span>, <span>$prepend</span>);
这下是不是很明白了,当实例化一个类的时候,如果这个类没有定义,就执行 ClassLoader 类中的 loadClass 函数。loadClass 的定义前面我们说过了,就是找到声明相关类的文件,然后包含并运行该文件,随后加载相关类。至此,composer的自动加载机制学习完毕。