Increase productivity with Zend Framework and collecting classes into a single file

Each time you run the link, and the server handles it ZendFramwork, nasty performance overhead when building the code executing the PHP interpreter.

Of course PHP can cache the opcode in memory using APC, Memcached, etc. But before you get opcode from memory there is a reference to the hard disk, to make sure updated if the last modified date. When the file is small, it is invisible. When they become a lot, starts to become a noticeable decrease in performance.

(APC of course you can set up your PHP interpreter does not check the date of files, but any file change you need to restart Apache, which is not convenient when developing'e).

The network has already met collectors classes, but they are not always properly collected what you need.

Yes, this topic has been raised many times, but I never found any script that would correctly connected classes Zend_Controller_Router_Route_Abstract and Zend_Controller_Router_Route_Chain.



Just to run through the files and collect them into one, to fail. In Zend the terms of use inheritance from classes and interfaces. And for proper compilation it is necessary that the class which comes after the implements or extends has been declared earlier in the code.

This feature is implemented in the following class and during Assembly, I don't think you will have problems.

So, the compiler can be run at the end of the execution of the script after the Zend_Controller_Front::getInstance()->dispatch(); (usually this line in index.php in the root folder of your project) using the following code:
ZCompile::make('d:/www/project/lib/zendframework-1.7.1/Zend.compiled.phplib'
array ()
'd:/www/project/lib/zendframework-1.7.1/'
);


* This source code was highlighted with Source Code Highlighter.


First parameter — specifies the path to the file which will contain all classes.
Second parameter — contains an array of additional files that you want to connect (about it later).
Third parameter — contains the path to the library of the framework in the end of mandatory forward slash '/'! This directory is called Zend. I.e. in my example, the class files Zend'a directory d:/www/project/lib/zendframework-1.7.1/Zend/. Again, you must specify the path only the folder to Zend.

After running the script, ZCompile will automatically take the list of required classes and write them in single file, respecting the inheritance hierarchy.

In the code I usually use the following construct, to select bootstrapping from the collected file or from the standard library:
if(FRAMEWORK_LOAD_COMPILED_ZEND==1) {
require 'Zend.compiled.phplib';
} else {
require 'Zend/Loader.php';
}
Zend_Loader::registerAutoload('Zend_Loader');


* This source code was highlighted with Source Code Highlighter.

Very convenient to store "compiled" file and class ZCompile on the same level together with the Zend folder (library, framework). Ie they at me are stored in the directory d:/www/project/lib/zendframework-1.7.1/

Now, remember I told you about second parameter, which is an array. So, it allows you to connect the classes that the current script is not used, but the project needs them.
There just define the basic classes or directories you want to connect. For example:
array(
'Zend/Auth/' // initialize classes from a directory, the slash is mandatory at the end of the
'Zend/Acl/' // initialize classes from a directory, the slash is mandatory at the end of the
'Zend/View/Helper/HeadTitle.php' // to connect a specific class
)


* This source code was highlighted with Source Code Highlighter.


When connecting a directory, then all files are read only at the current level, in the subdirectory we are not. When connected pure class, then read only it.
BUT in the connection class (via the directory or directly) ZCompile will gather all the classes necessary for work of the specified file. Ie if you specify the directory 'Zend/Auth/', then it'll probably just collect everything that is contained in the specified folder including sub-directories.

You can just in a separate file to run the following code, which simply will build you Acl and Auth:
<?php

set_include_path(
'd:/www/ksystem/lib/zendframework-1.7.1/'
. PATH_SEPARATOR . get_include_path());

ZCompile::make('d:/www/project/lib/zendframework-1.7.1/Zend.compiled.phplib'
array('Zend/Auth/' 'Zend/Acl/')
'd:/www/ksystem/lib/zendframework-1.7.1/'
);
?>


* This source code was highlighted with Source Code Highlighter.


So, the class itself ZCompile (errors in the comments I do not find fault, it's not important!, once they had to fix it ;) ):
<?php
/**
* Class to build modules ZendFramwork in a single file.
*
* @author Nod nodkz.at.mail.ru
*/
class ZCompile
{
static private $path;

/**
* Build sincluding files by removing comments, the require/include, and so on
*
* @param string $dest Absolute name of the file compilenode
* @param string $includes the Array of files to build ZF
* @return array
*/
public static function make($dest, array $add_includes = array(), $path = ")
{
self::$path=$path;
$includes=array_merge($add_includes, self::_scanFolderFiles($add_includes), self::_getZendIncludes());

//remove the slash at the beginning
foreach($includes as $key=>&$value) {
if(substr($value,0,1)=='/') {
$includes[$key] = substr($value,1);
}
}

// get the serial order download file
$ordered_include=Array();
foreach($includes as $class_file) {
self::_getClassOrderIncludes($class_file, $ordered_include);
}

// Remove tags '<?php' '? >', comments ,empty lines and transactions require/include[_once]
// and write to file
$pattern[] ='%(^\<\?php|\?\>$)%m';
$replacement[] = ";
$pattern[] ='%/\*.*?\*/%sm';
$replacement[] = ";
//$pattern[] ='%//.*$%m';
//$replacement[] = ";
$pattern[] ='%(require_once|include_once|require|include) [("\'](.*?)[)"\'];%sm';
$replacement[] = ";
$pattern[] ='%(\n){2,}%';
$replacement[] = "\n";

$body = "<?php\n";
$worked_classes = Array();
foreach ($ordered_include as &$fname) {
if(!in_array($fname, $worked_classes)) {
$worked_classes[] = $fname;

$fname = self::$path.$fname;
if(@file_exists($fname)&&is_file($fname)) {
$body.="/*** FILE: ".$fname." ***/ \r\n";
$body .= preg_replace($pattern, $replacement, file_get_contents($fname true));
}
}
}

$size = file_put_contents($dest, $body);

return array('includes' => $text 'compiledBody' => $body, 'compiledSize' => $size);
}

/**
* Protonirovanie names for compliance with the directory name (i.e. ends with /).
* And then take only those php files that are in that folder (no recursive pass).
* @param array $add_includes
* @return array
*/
private static function _scanFolderFiles(&$add_includes) {
$add_includes_dirs=array();
foreach($add_includes as $key=>$elem) {
if(substr($elem,-1)=='/') {
if (is_dir(self::$path.$elem)) {
if ($dh = opendir(self::$path.$elem)) {
while (($file = readdir($dh)) !== false) {
if(strpos(strtolower($file) '.php')!==false) {
$add_includes_dirs[]=$elem.$file;
}
}
closedir($dh);
}
}
$add_includes[$key]=";
}
}
return $add_includes_dirs;
}

/**
* Get the array of files which need to connect for work of the given file.
*
* @param string $fname
* @param array &$already_included
*/
private static function _getClassOrderIncludes($class_filename, array &$already_included=Array(), array &$stack=Array()) {
// check for infinite loop in recursion
if(!in_array($class_filename, $stack)) {
array_push($stack, $class_filename);
} else {
if(!in_array($class_filename, $already_included)) {
$already_included[]=$class_filename;
}
return;
}

// if the file exists and we haven't being parsed
if(is_file(self::$path.$class_filename) && !in_array($class_filename, $already_included)) {
$class_file_content = file_get_contents(self::$path.$class_filename true);

// allocated to class inheritance for initial connection
// ie, if the class extends using extends or implements,
// the extensible class is given to the file name and plug-in
if(preg_match_all('/class\s+[_\w]+\s+(extends|implements)\s+([_\w]+)/i', $class_file_content, $arr)) {
foreach($arr[2] as $new_class_name) {
$new_class_path = str_replace('_''/',$new_class_name).'.php';
if(!in_array($new_class_path, $already_included)) {
self::_getClassOrderIncludes($new_class_path, $already_included, $stack);
}
}
}

// pull out all the lines associated with connection of other files
if(preg_match_all('%(require_once|include_once|require|include) [("\'](.*?)[)"\'];%sm', $class_file_content, $arr)) {
// for each includinga file
foreach($arr[2] as $new_class_path) {
// check that we have not yet scanned, it is cheaper than to parse the file
if(!in_array($new_class_path, $already_included) && !in_array($new_class_path, $stack)) {
// check that the name was not variable
if(strpos($new_class_path '$')===false) {
// start earlier to connect, what to includethis in the current include
self::_getClassOrderIncludes($new_class_path, $already_included, $stack);
}
}
}
}
if(!in_array($class_filename, $already_included)) {
$already_included[]=$class_filename;
}
}

array_pop($stack);
}

/**
* Choose a unique file ZF singlegene in the project
*
* @return array
*/
private static _getZendIncludes function()
{
$required = array();
$included_files = get_included_files();
$included_files;
foreach ($included_files as $fname) {
$fname = str_replace('/''\\',$fname);

if (!(strpos($fname, '
\\Zend\\') > 0) || (strstr($fname, __CLASS__ . '.php'))) {
}

$required[] = str_replace('
\\', '/', substr($fname, strpos($fname, '\\Zend\\'), strlen($fname)));
}

return array_unique($required);
}
}
?>


* This source code was highlighted with Source Code Highlighter.


Download class

Yuzayte health.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Fresh hay from the cow, or 3000 icons submitted!

Knowledge base. Part 2. Freebase: make requests to the Google Knowledge Graph

Group edit the resources (documents) using MIGXDB