1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="performance.classloading">
4 <title>Class Loading</title>
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.
17 <sect2 id="performance.classloading.includepath">
18 <title>How can I optimize my include_path?</title>
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
30 <sect3 id="performance.classloading.includepath.abspath">
31 <title>Use absolute paths</title>
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
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:
48 <programlisting language="php"><![CDATA[
50 realpath(dirname(__FILE__) . '/../library'),
53 set_include_path(implode(PATH_SEPARATOR, $paths);
57 You <emphasis>can</emphasis> use relative paths -- so long as
58 they are relative to an absolute path:
61 <programlisting language="php"><![CDATA[
62 define('APPLICATION_PATH', realpath(dirname(__FILE__)));
64 APPLICATION_PATH . '/../library'),
67 set_include_path(implode(PATH_SEPARATOR, $paths);
71 However, even so, it's typically a trivial task to simply pass
72 the path to <methodname>realpath()</methodname>.
76 <sect3 id="performance.classloading.includepath.reduce">
77 <title>Reduce the number of include paths you define</title>
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.
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
101 <sect3 id="performance.classloading.includepath.early">
102 <title>Define your Zend Framework include_path as early as possible</title>
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.
113 <sect3 id="performance.classloading.includepath.currentdir">
114 <title>Define the current directory last, or not at all</title>
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.
127 <example id="performance.classloading.includepath.example">
128 <title>Example: Optimized include_path</title>
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.
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>
145 <programlisting language="php"><![CDATA[
154 This allows us to add our own library code if necessary, while
155 keeping shared libraries intact.
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.
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.
174 <programlisting language="php"><![CDATA[
176 realpath(dirname(__FILE__) . '/../library'),
179 set_include_path(implode(PATH_SEPARATOR, $paths));
185 <sect2 id="performance.classloading.striprequires">
186 <title>How can I eliminate unnecessary require_once statements?</title>
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
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?
206 <sect3 id="performance.classloading.striprequires.sed">
207 <title>Strip require_once calls with find and sed</title>
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):
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'
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
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
243 <programlisting language="php"><![CDATA[
244 require_once 'Zend/Loader/Autoloader.php';
245 Zend_Loader_Autoloader::getInstance();
250 <sect2 id="performance.classloading.pluginloader">
251 <title>How can I speed up plugin loading?</title>
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.
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.
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:
281 <classname>Zend_Controller_Action_HelperBroker</classname>: helpers
287 <classname>Zend_Dojo</classname>: view helpers, form elements and decorators
293 <classname>Zend_File_Transfer</classname>: adapters
299 <classname>Zend_Filter_Inflector</classname>: filters (used by the
300 ViewRenderer action helper and <classname>Zend_Layout</classname>)
306 <classname>Zend_Filter_Input</classname>: filters and validators
312 <classname>Zend_Form</classname>: elements, validators, filters,
313 decorators, captcha and file transfer adapters
319 <classname>Zend_Paginator</classname>: adapters
325 <classname>Zend_View</classname>: helpers, filters
331 How can you reduce the number of such calls made?
335 id="performance.classloading.pluginloader.includefilecache">
336 <title>Use the PluginLoader include file cache</title>
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.
347 The PluginLoader documentation <link
348 linkend="zend.loader.pluginloader.performance.example">includes
349 a complete example of its use</link>.