[GENERIC] Zend_Translate:
[zend.git] / documentation / manual / en / ref / performance-classloading.xml
blob1e1e53e1bbf1733f1c53ca7047c7753a9b5e7bd3
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- Reviewed: no -->
3 <sect1 id="performance.classloading">
4     <title>Class Loading</title>
6     <para>
7         Anyone who ever performs profiling of a Zend Framework application will
8         immediately recognize that class loading is relatively expensive in Zend
9         Framework. Between the sheer number of class files that need to be
10         loaded for many components, to the use of plugins that do not have a 1:1
11         relationship between their class name and the file system, the various
12         calls to <methodname>include_once()</methodname> and
13         <methodname>require_once()</methodname> can be problematic. This chapter intends to provide
14         some concrete solutions to these issues.
15     </para>
17     <sect2 id="performance.classloading.includepath">
18         <title>How can I optimize my include_path?</title>
20         <para>
21             One trivial optimization you can do to increase the speed of class
22             loading is to pay careful attention to your include_path. In
23             particular, you should do four things: use absolute paths (or paths
24             relative to absolute paths), reduce the number of include paths you
25             define, have your Zend Framework include_path as early as possible,
26             and only include the current directory path at the end of your
27             include_path.
28         </para>
30         <sect3 id="performance.classloading.includepath.abspath">
31             <title>Use absolute paths</title>
33             <para>
34                 While this may seem a micro-optimization, the fact is that if
35                 you don't, you'll get very little benefit from <acronym>PHP</acronym>'s realpath
36                 cache, and as a result, opcode caching will not perform nearly
37                 as you may expect.
38             </para>
40             <para>
41                 There are two easy ways to ensure this. First, you can hardcode
42                 the paths in your <filename>php.ini</filename>, <filename>httpd.conf</filename>,
43                 or <filename>.htaccess</filename>. Second, you
44                 can use <acronym>PHP</acronym>'s <methodname>realpath()</methodname> function when
45                 setting your include_path:
46             </para>
48             <programlisting language="php"><![CDATA[
49 $paths = array(
50     realpath(dirname(__FILE__) . '/../library'),
51     '.',
53 set_include_path(implode(PATH_SEPARATOR, $paths);
54 ]]></programlisting>
56             <para>
57                 You <emphasis>can</emphasis> use relative paths -- so long as
58                 they are relative to an absolute path:
59             </para>
61             <programlisting language="php"><![CDATA[
62 define('APPLICATION_PATH', realpath(dirname(__FILE__)));
63 $paths = array(
64     APPLICATION_PATH . '/../library'),
65     '.',
67 set_include_path(implode(PATH_SEPARATOR, $paths);
68 ]]></programlisting>
70             <para>
71                 However, even so, it's typically a trivial task to simply pass
72                 the path to <methodname>realpath()</methodname>.
73             </para>
74         </sect3>
76         <sect3 id="performance.classloading.includepath.reduce">
77             <title>Reduce the number of include paths you define</title>
79             <para>
80                 Include paths are scanned in the order in which they appear in
81                 the include_path. Obviously, this means that you'll get a result
82                 faster if the file is found on the first scan rather than the
83                 last. Thus, a rather obvious enhancement is to simply reduce the
84                 number of paths in your include_path to only what you need. Look
85                 through each include_path you've defined, and determine if you
86                 actually have any functionality in that path that is used in
87                 your application; if not, remove it.
88             </para>
90             <para>
91                 Another optimization is to combine paths. For instance, Zend
92                 Framework follows <acronym>PEAR</acronym> naming conventions; thus, if you are
93                 using <acronym>PEAR</acronym> libraries (or libraries from another framework or
94                 component library that follows <acronym>PEAR</acronym> CS), try to put all of these
95                 libraries on the same include_path. This can often be achieved
96                 by something as simple as symlinking one or more libraries into
97                 a common directory.
98             </para>
99         </sect3>
101         <sect3 id="performance.classloading.includepath.early">
102             <title>Define your Zend Framework include_path as early as possible</title>
104             <para>
105                 Continuing from the previous suggestion, another obvious
106                 optimization is to define your Zend Framework include_path as
107                 early as possible in your include_path. In most cases, it should
108                 be the first path in the list. This ensures that files included
109                 from Zend Framework are found on the first scan.
110             </para>
111         </sect3>
113         <sect3 id="performance.classloading.includepath.currentdir">
114             <title>Define the current directory last, or not at all</title>
116             <para>
117                 Most include_path examples show using the current directory, or
118                 '.'. This is convenient for ensuring that scripts in the same
119                 directory as the file requiring them can be loaded. However,
120                 these same examples typically show this path item as the first
121                 item in the include_path -- which means that the current
122                 directory tree is always scanned first. In most cases, with Zend
123                 Framework applications, this is not desired, and the path may be
124                 safely pushed to the last item in the list.
125             </para>
127             <example id="performance.classloading.includepath.example">
128                 <title>Example: Optimized include_path</title>
130                 <para>
131                     Let's put all of these suggestions together. Our assumption will be that you
132                     are using one or more <acronym>PEAR</acronym> libraries in conjunction with
133                     Zend Framework -- perhaps the PHPUnit and <classname>Archive_Tar</classname>
134                     libraries -- and that you occasionally need to include
135                     files relative to the current file.
136                 </para>
138                 <para>
139                     First, we'll create a library directory in our project. Inside that
140                     directory, we'll symlink our Zend Framework's <filename>library/Zend</filename>
141                     directory, as well as the necessary directories from our <acronym>PEAR</acronym>
142                     installation:
143                 </para>
145                 <programlisting language="php"><![CDATA[
146 library
147     Archive/
148     PEAR/
149     PHPUnit/
150     Zend/
151 ]]></programlisting>
153                 <para>
154                     This allows us to add our own library code if necessary, while
155                     keeping shared libraries intact.
156                 </para>
158                 <para>
159                     Next, we'll opt to create our include_path programmatically
160                     within our <filename>public/index.php</filename> file. This allows us to move
161                     our code around on the file system, without needing to edit the
162                     include_path every time.
163                 </para>
165                 <para>
166                     We'll borrow ideas from each of the suggestions above: we'll use
167                     absolute paths, as determined using <methodname>realpath()</methodname>;
168                     we'll include Zend Framework's include path early; we've
169                     already consolidated include_paths; and we'll put the current
170                     directory as the last path. In fact, we're doing really well
171                     here -- we're going to end up with only two paths.
172                 </para>
174                 <programlisting language="php"><![CDATA[
175 $paths = array(
176     realpath(dirname(__FILE__) . '/../library'),
177     '.'
179 set_include_path(implode(PATH_SEPARATOR, $paths));
180 ]]></programlisting>
181             </example>
182         </sect3>
183     </sect2>
185     <sect2 id="performance.classloading.striprequires">
186         <title>How can I eliminate unnecessary require_once statements?</title>
188         <para>
189             Lazy loading is an optimization technique designed to push the
190             expensive operation of loading a class file until the last possible
191             moment -- i.e., when instantiating an object of that class, calling
192             a static class method, or referencing a class constant or static
193             property. <acronym>PHP</acronym> supports this via autoloading, which allows you to
194             define one or more callbacks to execute in order to map a class name
195             to a file.
196         </para>
198         <para>
199             However, most benefits you may reap from autoloading are negated if
200             your library code is still performing <methodname>require_once()</methodname> calls --
201             which is precisely the case with Zend Framework. So, the question is: how can
202             you eliminate those <methodname>require_once()</methodname> calls in order to maximize
203             autoloader performance?
204         </para>
206         <sect3 id="performance.classloading.striprequires.sed">
207             <title>Strip require_once calls with find and sed</title>
209             <para>
210                 An easy way to strip <methodname>require_once()</methodname> calls is to use the
211                 <acronym>UNIX</acronym> utilities 'find' and 'sed' in conjunction to comment out
212                 each call. Try executing the following statements (where '%'
213                 indicates the shell prompt):
214             </para>
216             <programlisting language="shell"><![CDATA[
217 % cd path/to/ZendFramework/library
218 % find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \
219   -not -wholename '*/Application.php' -print0 | \
220   xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
221 ]]></programlisting>
223             <para>
224                 This one-liner (broken into two lines for readability) iterates
225                 through each <acronym>PHP</acronym> file and tells it to replace each instance of
226                 'require_once' with '// require_once', effectively commenting
227                 out each such statement. (It selectively keeps
228                 <methodname>require_once()</methodname> calls within
229                 <classname>Zend_Application</classname> and
230                 <classname>Zend_Loader_Autoloader</classname>, as these classes will fail without
231                 them.)
232             </para>
234             <para>
235                 This command could be added to an automated build or release
236                 process trivially, helping boost performance in your production
237                 application. It should be noted, however, that if you use this
238                 technique, you <emphasis>must</emphasis> utilize autoloading;
239                 you can do that from your "<filename>public/index.php</filename>" file with the
240                 following code:
241             </para>
243             <programlisting language="php"><![CDATA[
244 require_once 'Zend/Loader/Autoloader.php';
245 Zend_Loader_Autoloader::getInstance();
246 ]]></programlisting>
247         </sect3>
248     </sect2>
250     <sect2 id="performance.classloading.pluginloader">
251         <title>How can I speed up plugin loading?</title>
253         <para>
254             Many components have plugins, which allow you to create your own
255             classes to utilize with the component, as well as to override
256             existing, standard plugins shipped with Zend Framework. This
257             provides important flexibility to the framework, but at a price:
258             plugin loading is a fairly expensive task.
259         </para>
261         <para>
262             The plugin loader allows you to register class prefix / path pairs,
263             allowing you to specify class files in non-standard paths. Each
264             prefix can have multiple paths associated with it.
265             Internally, the plugin loader loops through each prefix, and then
266             through each path attached to it, testing to see if the file exists
267             and is readable on that path. It then loads it, and tests to see
268             that the class it is looking for is available. As you might imagine,
269             this can lead to many stat calls on the file system.
270         </para>
272         <para>
273             Multiply this by the number of components that use the PluginLoader,
274             and you get an idea of the scope of this issue. At the time of this
275             writing, the following components made use of the PluginLoader:
276         </para>
278         <itemizedlist>
279             <listitem>
280                 <para>
281                     <classname>Zend_Controller_Action_HelperBroker</classname>: helpers
282                 </para>
283             </listitem>
285             <listitem>
286                 <para>
287                     <classname>Zend_Dojo</classname>: view helpers, form elements and decorators
288                 </para>
289             </listitem>
291             <listitem>
292                 <para>
293                     <classname>Zend_File_Transfer</classname>: adapters
294                 </para>
295             </listitem>
297             <listitem>
298                 <para>
299                     <classname>Zend_Filter_Inflector</classname>: filters (used by the
300                     ViewRenderer action helper and <classname>Zend_Layout</classname>)
301                 </para>
302             </listitem>
304             <listitem>
305                 <para>
306                     <classname>Zend_Filter_Input</classname>: filters and validators
307                 </para>
308             </listitem>
310             <listitem>
311                 <para>
312                     <classname>Zend_Form</classname>: elements, validators, filters,
313                     decorators, captcha and file transfer adapters
314                 </para>
315             </listitem>
317             <listitem>
318                 <para>
319                     <classname>Zend_Paginator</classname>: adapters
320                 </para>
321             </listitem>
323             <listitem>
324                 <para>
325                     <classname>Zend_View</classname>: helpers, filters
326                 </para>
327             </listitem>
328         </itemizedlist>
330         <para>
331             How can you reduce the number of such calls made?
332         </para>
334         <sect3
335             id="performance.classloading.pluginloader.includefilecache">
336             <title>Use the PluginLoader include file cache</title>
338             <para>
339                 Zend Framework 1.7.0 adds an include file cache to the
340                 PluginLoader. This functionality writes "<methodname>include_once()</methodname>"
341                 calls to a file, which you can then include in your bootstrap. While this
342                 introduces extra <methodname>include_once()</methodname> calls to your code, it
343                 also ensures that the PluginLoader returns as early as possible.
344             </para>
346             <para>
347                 The PluginLoader documentation <link
348                     linkend="zend.loader.pluginloader.performance.example">includes
349                 a complete example of its use</link>.
350             </para>
351         </sect3>
352     </sect2>
353 </sect1>
354 <!--
355 vim:se ts=4 sw=4 et: