samsonos/php_core

View on GitHub
src/deprecated/AutoLoader.php

Summary

Maintainability
B
5 hrs
Test Coverage
<?php
/**
 * Created by Vitaly Iegorov <egorov@samsonos.com>
 * on 24.04.14 at 18:09
 */
namespace samson\core;

/**
 * Generic SamsonPHP class auto loader
 * @author Vitaly Egorov <egorov@samsonos.com>
 * @copyright 2014 SamsonOS
 * @version 1.0.0
 * @deprecated All loading logic should go to composer
 */
class AutoLoader
{
    /** Namespace separator character marker */
    const NS_SEPARATOR = '\\';

    /** @var array Static collection module locations */
    public static $moduleMap = array();

    /** Module files cache for optimizing */
    protected static $fileCache = array();

    /** Loaded namespaces cache */
    protected static $nameSpaces = array();

    /**
     * Generate "correct" class name dependently on the current PHP version,
     * if PHP version is lower 5.3.0 it does not supports namespaces and function
     * will convert class name with namespace to class name
     *
     * @param string $className Class name
     * @param string $namespace Namespace to add to class name if it does not have one
     *
     * @return string Supported class name
     */
    public static function className($className, $namespace = null)
    {
        // If passed class exists - just return it(without autoloading)
        if (class_exists($className, false)) {
            return $className;
        } else if (strpos($className, '\\') === false) { // If class name has no namespace separator
            // Add namespace to it
            $className = $namespace . '\\' . $className;
        }

        // If this is an old PHP version - remove namespaces
        return __SAMSON_PHP_OLD ? self::oldClassName($className) : $className;
    }

    /**
     * Convert class name to old PHP format not supporting name spaces
     *
     * @param string $className Class name to convert
     *
     * @return string Class name in old format where "\" replaced with "_"
     */
    public static function oldClassName($className)
    {
        // If class name has first symbol as NS_SEPARATOR - remove it to avoid ugly classes _samson_core_...
        if ($className{0} == self::NS_SEPARATOR) {
            $className = substr($className, 1);
        }

        // Convert NS_SEPARATOR to namespace
        return strtolower(str_replace(self::NS_SEPARATOR, '_', $className));
    }

    /**
     * Auto loader main logic
     *
     * @param string $class Full class name with namespace
     *
     * @return bool False if something went wrong
     */
    public static function load($class)
    {
        // Get just class name without ns
        $className = self::getOnlyClass($class);
        // Get just ns without class name
        $nameSpace = self::getOnlyNameSpace($class);

        // If this is not core class
        if ($nameSpace != __NAMESPACE__) {
            // Convert namespace to base directory add class
            $path = __SAMSON_VENDOR_PATH . str_replace('\\', DIRECTORY_SEPARATOR, $nameSpace) . DIRECTORY_SEPARATOR . $className . '.php';

            // Load class by file name
            if (file_exists($path)) {
                //elapsed('Autoloading class '.$class.' at '.$path);
                require_once($path);
                // old school compatibility will be removed when old modules will be updated
            } else if (self::oldModule($className, $nameSpace, $path)) {
                //elapsed('Autoloading(old style) class '.$class.' at '.$path);
                require_once($path);
                // Handle old module loading
            } else { // Signal error
                return e('Class name ## not found', E_SAMSON_CORE_ERROR, $class);
            }

        } else { // Load core classes separately using this class location
            require_once(__DIR__ . '/' . $className . '.php');
        }

        return true;
    }

    /**
     * Return only class name without namespace
     *
     * @param string $className Full class name with namespace
     *
     * @return string Class name without namespace
     */
    public static function getOnlyClass($className)
    {
        // Try to find namespace separator
        if (($p = strrpos($className, self::NS_SEPARATOR)) !== false) {
            $className = substr($className, $p + 1);
        }

        return $className;
    }

    /**
     * Return only namespace name from class name
     *
     * @param string $className Full class name with namespace
     *
     * @return string Namespace without class
     */
    public static function getOnlyNameSpace($className)
    {
        // Try to find namespace separator
        if (($p = strrpos($className, self::NS_SEPARATOR)) !== false) {
            $className = substr($className, 0, $p);
        }

        return $className;
    }

    /**
     * All our modules do not follow PSR-0 and classes located as they wish to, so we will have
     * to scan all files in module location and build all classes tree.
     *
     * @param string $className Class name without namespace
     * @param string $nameSpace Namespace name without class name
     * @param string $file      Variable to return path to class file location on success
     *
     * @deprecated Should be removed after all modules will be moved to PSR class naming and locating standard
     * @return bool True if class file is found
     */
    protected static function oldModule($className, $nameSpace, & $file = null)
    {
        //elapsed('++ Autoloading '.$value.' from '.$nameSpace);

        // Convert to linux path, windows will convert it automatically if necessary
        $ns = str_replace(self::NS_SEPARATOR, '/', $nameSpace);

        // Check if we have predefined namespace path definition
        if (isset(self::$moduleMap[$nameSpace])) {
            // take path from module location map
            $path = self::$moduleMap[$nameSpace];
        } else { // Try automatic searcher

            /** @var string[] $locations Collection of possible class locations */
            $locations = null;
            // Iterate all possible file structures
            $path = null;
            foreach (array('php', 'js', 'cms', 'social', 'commerce') as $type) {

                // Build all possible module location for backward compatibility
                $locations = array(
                    __SAMSON_VENDOR_PATH . str_replace('samson/', 'samsonos/', $ns) . '/' . strtolower($className),
                    __SAMSON_VENDOR_PATH . str_replace('samson/', 'samsonos/', $ns),
                    __SAMSON_VENDOR_PATH . str_replace('samson/', 'samsonos/' . $type . '/', $ns),
                    __SAMSON_VENDOR_PATH . str_replace('samson/', 'samsonos/' . $type . '/', $ns) . '/api',
                    __SAMSON_VENDOR_PATH . str_replace('samson/', 'samsonos/' . $type . '_', $ns),
                    strpos($ns, 'cms') !== false ? __SAMSON_VENDOR_PATH . 'samsonos/cms_api' : '_', // use cms api if class name has "cms"
                    strpos($ns, 'cms') !== false ? __SAMSON_VENDOR_PATH . 'samsonos/cms/api' : '_', // use cms api if class name has "cms"
                    __SAMSON_CWD__ . __SAMSON_MODEL_PATH, // use local model path as the last variant of class location
                );

                // Iterate all locations and try to find correct existing path
                foreach ($locations as $location) {
                    if (file_exists($location)) {
                        $path = $location;
                        break 2;
                    }
                }
            }
        }

        // If class not found
        if (isset($path)) {
            $path .= '/';

            // Build files tree once for each namespace
            if (!isset(self::$fileCache[$nameSpace])) {
                self::$fileCache[$nameSpace] = File::dir($path, 'php');
            }

            // Trying to find class by class name in folder files collection
            if (sizeof($files = preg_grep('/\/' . $className . '\.php/i', self::$fileCache[$nameSpace]))) {
                // If we have found several files matching this class
                if (sizeof($files) > 1) {
                    return e('Cannot autoload class(##), too many files matched ##', E_SAMSON_CORE_ERROR, array($className, $files));
                }

                // Return last array element as file path
                $file = end($files);

                //elapsed('  Loaded class['.$key.'] from "'.$file.'"');

                // Everything is OK
                return true;
            }
        }

        // If we are here - signal error
        //return e('Cannot autoload class(##), class file not found in ##', D_SAMSON_DEBUG, array($value,$path));
        return false;
    }
}