/classes/Fuel/Kernel/Loader/Package.php
PHP | 574 lines | 287 code | 45 blank | 242 comment | 16 complexity | 470eab52b517b3b489ab794e2c7030c4 MD5 | raw file
- <?php
- /**
- * Part of the FuelPHP framework.
- *
- * @package Fuel\Kernel
- * @version 2.0.0
- * @license MIT License
- * @copyright 2010 - 2012 Fuel Development Team
- */
- namespace Fuel\Kernel\Loader;
- use Fuel\Kernel\Environment;
- /**
- * Package Loader
- *
- * Default Fuel Package loader class that allows loading files & classes.
- *
- * @package Fuel\Kernel
- *
- * @since 2.0.0
- */
- class Package implements Loadable
- {
- /**
- * @var string name of this loader
- *
- * @since 2.0.0
- */
- public $name;
- /**
- * @var \Fuel\Kernel\Environment
- */
- protected $env;
- /**
- * @var string basepath for the package
- *
- * @since 2.0.0
- */
- protected $path = '';
- /**
- * @var string base namespace for the package (with trailing backslash when not empty)
- *
- * @since 2.0.0
- */
- protected $namespace = '';
- /**
- * @var string string to prefix the Controller classname with, will be relative to the base namespace
- *
- * @since 2.0.0
- */
- protected $classPrefixes = array(
- 'application' => 'Application\\',
- 'controller' => 'Controller\\',
- 'model' => 'Model\\',
- 'presenter' => 'Presenter\\',
- 'task' => 'Task\\',
- );
- /**
- * @var array package modules with array(relative path => relative subnamespace) (with trailing backslash)
- *
- * @since 2.0.0
- */
- protected $modules = array();
- /**
- * @var array registered classes, without the base namespace
- *
- * @since 2.0.0
- */
- protected $classes = array();
- /**
- * @var array classes that are aliased: classname => actual class
- *
- * @since 2.0.0
- */
- protected $classAliases = array();
- /**
- * @var bool whether the class's path is relative to the main namespace or fully PSR-0 compliant
- *
- * @since 2.0.0
- */
- protected $relativeClassLoad = false;
- /**
- * @var bool whether this package is routable
- *
- * @since 2.0.0
- */
- protected $routable = false;
- /**
- * @var string a first segment required for find_file() & find_class() that will be stripped
- *
- * @since 2.0.0
- */
- protected $findTrigger;
- /**
- * Magic Fuel method that is the setter for the current Environment
- *
- * @param \Fuel\Kernel\Environment $env
- * @return void
- *
- * @since 2.0.0
- */
- public function _setEnv(Environment $env)
- {
- $this->env = $env;
- // Show package loads inside application
- ($app = $env->activeApplication())
- and $app->notify('packageLoaderCreated', $this, __METHOD__);
- }
- /**
- * Assigns a name to this package
- *
- * @param string $name
- * @return Loadable
- *
- * @since 2.0.0
- */
- public function setName($name)
- {
- $this->name = $name;
- return $this;
- }
- /**
- * Returns the base namespace for this package
- *
- * @return string
- *
- * @since 2.0.0
- */
- public function getNamespace()
- {
- return $this->namespace;
- }
- /**
- * Returns the base path for this package
- *
- * @return string
- *
- * @since 2.0.0
- */
- public function getPath()
- {
- return $this->path;
- }
- /**
- * Attempt to load a class from the package
- *
- * @param string $class
- * @return bool
- *
- * @since 2.0.0
- */
- public function loadClass($class)
- {
- // Save the original classname
- $original = $class;
- // Check if the class path was registered with the Package
- if (isset($this->classes[$class]))
- {
- require $this->classes[$class];
- return true;
- }
- // Check if the request class is an alias registered with the Package
- elseif (isset($this->classAliases[$class]))
- {
- class_alias($this->classAliases[$class], $class);
- return true;
- }
- // If a base namespace was set and doesn't match the class: fail
- if ($this->namespace === false
- or ($this->namespace and strpos($class, $this->namespace) !== 0))
- {
- return false;
- }
- // Anything further will be relative to the base namespace
- $class = substr($class, strlen($this->namespace));
- // Check if any of the modules' namespaces matches the class and make it relative on such a match
- $path = $this->path;
- foreach ($this->modules as $mPath => $mNamespace)
- {
- if (strpos($class, $mNamespace) === 0)
- {
- $class = substr($class, strlen($mNamespace));
- $path .= 'modules/'.$mPath.'/';
- break;
- }
- }
- $path = $this->classToPath($original, $class, $path.'classes/');
- // When found include the file and return success
- if (is_file($path))
- {
- require $path;
- return true;
- }
- // ... still here? Failure.
- return false;
- }
- /**
- * Converts a classname to a path using PSR-0 conventions
- *
- * NOTE: using the base namespace setting and usage of modules break PSR-0 convention. The paths are expected
- * relative to the base namespace when used and optionally relative to the module's (sub)namespace.
- *
- * @param string $fullName full classname
- * @param string $class classname relative to base/module namespace
- * @param string $basePath
- * @return string
- *
- * @since 2.0.0
- */
- protected function classToPath($fullName, $class, $basePath)
- {
- return $basePath.$this->env->loader->psrClassToPath($this->relativeClassLoad ? $class : $fullName);
- }
- /**
- * Set a base path for the package
- *
- * @param string $path
- * @return Package
- *
- * @since 2.0.0
- */
- public function setPath($path)
- {
- $this->path = rtrim($path, '/\\').'/';
- return $this;
- }
- /**
- * Set a base namespace for the package, only classes from that namespace are loaded
- *
- * @param string $namespace
- * @return Package
- *
- * @since 2.0.0
- */
- public function setNamespace($namespace)
- {
- $this->namespace = $namespace ? trim($namespace, '\\').'\\' : $namespace;
- return $this;
- }
- /**
- * Add a module with path & namespace
- *
- * @param string $path
- * @param string $namespace
- * @return Package
- *
- * @since 2.0.0
- */
- public function addModule($path, $namespace)
- {
- $this->modules = array(trim($path, '/\\').'/' => trim($namespace, '\\').'\\') + $this->modules;
- return $this;
- }
- /**
- * Remove a module from the package
- *
- * @param string $path
- * @return Package
- *
- * @since 2.0.0
- */
- public function removeModule($path)
- {
- unset($this->modules[trim($path, '/\\').'/']);
- return $this;
- }
- /**
- * Adds a class to the Package that doesn't need to be found
- *
- * @param string $class
- * @param string $path
- * @return Package
- *
- * @since 2.0.0
- */
- public function addClass($class, $path)
- {
- return $this->addClasses(array($class => $path));
- }
- /**
- * Adds classes to the Package that don't need to be found
- *
- * @param array $classes
- * @return Package
- *
- * @since 2.0.0
- */
- public function addClasses(array $classes)
- {
- foreach ($classes as $class => $path)
- {
- $this->classes[$class] = $path;
- }
- return $this;
- }
- /**
- * Add an alias and the actual classname
- *
- * @param string $alias
- * @param string $actual
- * @return Package for method chaining
- *
- * @since 2.0.0
- */
- public function addClassAlias($alias, $actual)
- {
- return $this->addClassAliases(array($alias => $actual));
- }
- /**
- * Add multiple classes with their aliases
- *
- * @param array $classes
- * @return Package for method chaining
- *
- * @since 2.0.0
- */
- public function addClassAliases(array $classes = array())
- {
- foreach ($classes as $alias => $actual)
- {
- $this->classAliases[$alias] = $actual;
- }
- return $this;
- }
- /**
- * Removes a class from the package
- *
- * @param string $class
- * @return Package
- *
- * @since 2.0.0
- */
- public function removeClass($class)
- {
- unset($this->classes[$class]);
- return $this;
- }
- /**
- * Sets routability of this package
- *
- * @param bool $routable
- * @return Loadable
- *
- * @since 2.0.0
- */
- public function setRoutable($routable)
- {
- $this->routable = (bool) $routable;
- return $this;
- }
- /**
- * Sets whether a class's path is relative to the main namespace of this
- * package (true) or normal PSR-0 (false)
- *
- * @param bool $compliance
- * @return Loadable
- *
- * @since 2.0.0
- */
- public function setRelativeClassLoad($compliance)
- {
- $this->relativeClassLoad = (bool) $compliance;
- return $this;
- }
- /**
- * Sets the find trigger: a firgst segment required for find_file() and find_class()
- *
- * @param bool $trigger
- * @return Package
- *
- * @since 2.0.0
- */
- public function setFindTrigger($trigger)
- {
- $this->findTrigger = trim(strval($trigger), '\\/');
- return $this;
- }
- /**
- * Change a special class type prefix
- *
- * @param string $type
- * @param string $prefix
- * @return Package
- *
- * @since 2.0.0
- */
- public function setClassTypePrefix($type, $prefix)
- {
- $this->classPrefixes[strtolower($type)] = $prefix;
- return $this;
- }
- /**
- * Get the class prefix for a specific type
- *
- * @param string $type
- * @return string
- *
- * @since 2.0.0
- */
- public function classTypePrefix($type)
- {
- $type = strtolower($type);
- return isset($this->classPrefixes[$type]) ? $this->classPrefixes[$type] : '';
- }
- /**
- * Attempts to find a controller, loads the class and returns the classname if found
- *
- * @param string $type for example: controller or task
- * @param string $path
- * @return bool|string
- *
- * @since 2.0.0
- */
- public function findClass($type, $path)
- {
- // If the routable property is a string then this requires a trigger segment to be findable
- if (is_string($this->findTrigger))
- {
- // If string trigger isn't found at the beginning return false
- if (strpos(strtolower($path), strtolower($this->findTrigger).'/') !== 0)
- {
- return false;
- }
- // Strip trigger from classname
- $path = substr($path, strlen($this->findTrigger) + 1);
- }
- // Build the namespace for the controller
- $namespace = $this->namespace;
- if ($pos = strpos($path, '/'))
- {
- $module = substr($path, 0, $pos).'/';
- if (isset($this->modules[$module]))
- {
- $namespace .= $this->modules[$module];
- $path = substr($path, $pos + 1);
- }
- }
- $path = $namespace.$this->classTypePrefix($type).str_replace('/', '_', $path);
- if ($this->loadClass($path))
- {
- return $path;
- }
- return false;
- }
- /**
- * Attempts to find a specific file
- *
- * @param string $file
- * @return bool|string
- *
- * @since 2.0.0
- */
- public function findFile($file)
- {
- // if the routable property is a string then this requires a trigger segment to be findable
- if (is_string($this->findTrigger))
- {
- // if string trigger isn't found at the beginning return false
- $fileSegments = explode('/', $file);
- if (count($fileSegments) <= 2 or $fileSegments[1] !== $this->findTrigger)
- {
- return false;
- }
- // strip trigger from file path
- unset($fileSegments[1]);
- $file = implode('/', $fileSegments);
- }
- // if given attempt specific module load
- if (($pos = strpos($file, ':')) !== false)
- {
- $module = substr($file, 0, $pos).'/';
- if (isset($this->modules[$module]))
- {
- if (is_file($path = $this->path.$module.substr($file, $pos + 1)))
- {
- return $path;
- }
- }
- return false;
- }
- // attempt fetch from base
- if (file_exists($path = $this->path.$file))
- {
- return $path;
- }
- // attempt to find in modules
- foreach ($this->modules as $path => $ns)
- {
- if (file_exists($path = $this->path.'modules/'.$path.$file))
- {
- return $path;
- }
- }
- // all is lost
- return false;
- }
- /**
- * Glob package and module dirs
- * Returns result of each glob indexed by full path to the dir
- *
- * @param string $location
- * @param string $filter
- * @param int $flags
- * @return array combined glob result
- *
- * @since 2.0.0
- */
- public function glob($location, $filter, $flags = 0)
- {
- $location = trim($location, '\\/').'/';
- $glob = array();
- // First add the main dir
- $path = $this->path.$location;
- $glob[$path] = glob($path, $flags);
- // Add the modules
- foreach ($this->modules as $path => $ns)
- {
- $path = $this->path.'modules/'.$path.$location;
- $glob[$path] = glob($path.$filter, $flags);
- }
- return $glob;
- }
- }