<?php
/**
 * Generates array of classes for autoload
 *
 * @copyright NetMonsters <team@netmonsters.ru>
 * @link http://netmonsters.ru
 * @package Majestic
 * @subpackage util
 * @since 2010-02-24
 * @version SVN: $Id$
 * @filesource $URL$
 */

/**
 * @todo Refactoring writing, locking
 */
class AutoloadBuilder
{
    protected $autoload;

    protected $dirs;

    protected $exclude;

    protected $handler;

    protected $regex = '/(class|interface) ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/';

    public function __construct($autoload, $dirs = array(), $exclude = array())
    {
        $this->autoload = $autoload;
        $this->dirs = $dirs;
        $this->exclude = $exclude;
    }

    public function build()
    {
        $array_string = "<?php\n// This file is autogenerated by \n// " . __FILE__ . " script.\nreturn array(\n";
        // for dublicates check
        $classes = array();
        foreach ($this->dirs as $dir) {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($dir),
                RecursiveIteratorIterator::LEAVES_ONLY
            );

            foreach ($iterator as $file) {

                if ($this->isExcluded($file->getRealPath())) {
                    continue;
                }
                // skip non php files
                $e = explode('.', $file->getFileName());
                if ((end($e) !== 'php') || $this->rightSubstr($file->getFileName(), 8) == 'Test.php') {
                    continue;
                }

                $content = file_get_contents($file->getRealPath());
                $matches = array();

                $relative_path = substr($file->getRealPath(), strlen(PATH));

                if (preg_match_all($this->regex, $content, $matches, PREG_SET_ORDER)) {
                    foreach ($matches as $match) {
                        if (array_key_exists($match[2], $classes)) {
                            continue;
                        }
                        $array_string .= "'{$match[2]}'=>'" . $relative_path . "',\n";
                        $classes[$match[2]] = $file->getRealPath();
                    }
                }
            }
            unset($file);   // without this line trigger_error throws 'Serialization of 'SplFileInfo' is not allowed' exception under PHPUnit
        }
        $array_string .= ');';
        $this->writeAutoload($array_string);
        if (!$this->isFileConsistent()) {
            unlink($this->autoload);
            trigger_error("Error while generating autoload file.", E_USER_ERROR);
        }
    }

    protected function writeAutoload($string)
    {
        $pid = getmypid();
        $tmp = dirname($this->autoload) . '/' . md5(time() + $pid);
        file_put_contents($tmp, $string);
        rename($tmp, $this->autoload);
    }

    protected function isFileConsistent()
    {
        $autoload_array = include($this->autoload);
        if (!is_array($autoload_array)) {
            return false;
        }
        return true;
    }

    protected function isExcluded($file)
    {
        foreach ($this->exclude as $dir) {
            if (stripos($file, $dir) === 0) {
                return true;
            }
        }
        return false;
    }

    protected function rightSubstr($string, $nchars)
    {
        $right = substr($string, strlen($string) - $nchars, $nchars);
        return $right;
    }
}