Added Zend Framework 2.1.2 classes for thread-safe gettext-format translation (TODO: use autoloader)
/trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Translator.php |
---|
0,0 → 1,586 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\I18n\Translator; |
use Locale; |
use Traversable; |
use Zend\Cache; |
use Zend\Cache\Storage\StorageInterface as CacheStorage; |
use Zend\I18n\Exception; |
use Zend\I18n\Translator\Loader\FileLoaderInterface; |
use Zend\I18n\Translator\Loader\RemoteLoaderInterface; |
use Zend\Stdlib\ArrayUtils; |
/** |
* Translator. |
*/ |
class Translator |
{ |
/** |
* Messages loaded by the translator. |
* |
* @var array |
*/ |
protected $messages = array(); |
/** |
* Files used for loading messages. |
* |
* @var array |
*/ |
protected $files = array(); |
/** |
* Patterns used for loading messages. |
* |
* @var array |
*/ |
protected $patterns = array(); |
/** |
* Remote locations for loading messages. |
* |
* @var array |
*/ |
protected $remote = array(); |
/** |
* Default locale. |
* |
* @var string |
*/ |
protected $locale; |
/** |
* Locale to use as fallback if there is no translation. |
* |
* @var string |
*/ |
protected $fallbackLocale; |
/** |
* Translation cache. |
* |
* @var CacheStorage |
*/ |
protected $cache; |
/** |
* Plugin manager for translation loaders. |
* |
* @var LoaderPluginManager |
*/ |
protected $pluginManager; |
/** |
* Instantiate a translator |
* |
* @param array|Traversable $options |
* @return Translator |
* @throws Exception\InvalidArgumentException |
*/ |
public static function factory($options) |
{ |
if ($options instanceof Traversable) { |
$options = ArrayUtils::iteratorToArray($options); |
} elseif (!is_array($options)) { |
throw new Exception\InvalidArgumentException(sprintf( |
'%s expects an array or Traversable object; received "%s"', |
__METHOD__, |
(is_object($options) ? get_class($options) : gettype($options)) |
)); |
} |
$translator = new static(); |
// locales |
if (isset($options['locale'])) { |
$locales = (array) $options['locale']; |
$translator->setLocale(array_shift($locales)); |
if (count($locales) > 0) { |
$translator->setFallbackLocale(array_shift($locales)); |
} |
} |
// file patterns |
if (isset($options['translation_file_patterns'])) { |
if (!is_array($options['translation_file_patterns'])) { |
throw new Exception\InvalidArgumentException( |
'"translation_file_patterns" should be an array' |
); |
} |
$requiredKeys = array('type', 'base_dir', 'pattern'); |
foreach ($options['translation_file_patterns'] as $pattern) { |
foreach ($requiredKeys as $key) { |
if (!isset($pattern[$key])) { |
throw new Exception\InvalidArgumentException( |
"'{$key}' is missing for translation pattern options" |
); |
} |
} |
$translator->addTranslationFilePattern( |
$pattern['type'], |
$pattern['base_dir'], |
$pattern['pattern'], |
isset($pattern['text_domain']) ? $pattern['text_domain'] : 'default' |
); |
} |
} |
// files |
if (isset($options['translation_files'])) { |
if (!is_array($options['translation_files'])) { |
throw new Exception\InvalidArgumentException( |
'"translation_files" should be an array' |
); |
} |
$requiredKeys = array('type', 'filename'); |
foreach ($options['translation_files'] as $file) { |
foreach ($requiredKeys as $key) { |
if (!isset($file[$key])) { |
throw new Exception\InvalidArgumentException( |
"'{$key}' is missing for translation file options" |
); |
} |
} |
$translator->addTranslationFile( |
$file['type'], |
$file['filename'], |
isset($file['text_domain']) ? $file['text_domain'] : 'default', |
isset($file['locale']) ? $file['locale'] : null |
); |
} |
} |
// remote |
if (isset($options['remote_translation'])) { |
if (!is_array($options['remote_translation'])) { |
throw new Exception\InvalidArgumentException( |
'"remote_translation" should be an array' |
); |
} |
$requiredKeys = array('type'); |
foreach ($options['remote_translation'] as $remote) { |
foreach ($requiredKeys as $key) { |
if (!isset($remote[$key])) { |
throw new Exception\InvalidArgumentException( |
"'{$key}' is missing for remote translation options" |
); |
} |
} |
$translator->addRemoteTranslations( |
$remote['type'], |
isset($remote['text_domain']) ? $remote['text_domain'] : 'default' |
); |
} |
} |
// cache |
if (isset($options['cache'])) { |
if ($options['cache'] instanceof CacheStorage) { |
$translator->setCache($options['cache']); |
} else { |
$translator->setCache(Cache\StorageFactory::factory($options['cache'])); |
} |
} |
return $translator; |
} |
/** |
* Set the default locale. |
* |
* @param string $locale |
* @return Translator |
*/ |
public function setLocale($locale) |
{ |
$this->locale = $locale; |
return $this; |
} |
/** |
* Get the default locale. |
* |
* @return string |
*/ |
public function getLocale() |
{ |
if ($this->locale === null) { |
$this->locale = Locale::getDefault(); |
} |
return $this->locale; |
} |
/** |
* Set the fallback locale. |
* |
* @param string $locale |
* @return Translator |
*/ |
public function setFallbackLocale($locale) |
{ |
$this->fallbackLocale = $locale; |
return $this; |
} |
/** |
* Get the fallback locale. |
* |
* @return string |
*/ |
public function getFallbackLocale() |
{ |
return $this->fallbackLocale; |
} |
/** |
* Sets a cache |
* |
* @param CacheStorage $cache |
* @return Translator |
*/ |
public function setCache(CacheStorage $cache = null) |
{ |
$this->cache = $cache; |
return $this; |
} |
/** |
* Returns the set cache |
* |
* @return CacheStorage The set cache |
*/ |
public function getCache() |
{ |
return $this->cache; |
} |
/** |
* Set the plugin manager for translation loaders |
* |
* @param LoaderPluginManager $pluginManager |
* @return Translator |
*/ |
public function setPluginManager(LoaderPluginManager $pluginManager) |
{ |
$this->pluginManager = $pluginManager; |
return $this; |
} |
/** |
* Retrieve the plugin manager for translation loaders. |
* |
* Lazy loads an instance if none currently set. |
* |
* @return LoaderPluginManager |
*/ |
public function getPluginManager() |
{ |
if (!$this->pluginManager instanceof LoaderPluginManager) { |
$this->setPluginManager(new LoaderPluginManager()); |
} |
return $this->pluginManager; |
} |
/** |
* Translate a message. |
* |
* @param string $message |
* @param string $textDomain |
* @param string $locale |
* @return string |
*/ |
public function translate($message, $textDomain = 'default', $locale = null) |
{ |
$locale = ($locale ?: $this->getLocale()); |
$translation = $this->getTranslatedMessage($message, $locale, $textDomain); |
if ($translation !== null && $translation !== '') { |
return $translation; |
} |
if (null !== ($fallbackLocale = $this->getFallbackLocale()) |
&& $locale !== $fallbackLocale |
) { |
return $this->translate($message, $textDomain, $fallbackLocale); |
} |
return $message; |
} |
/** |
* Translate a plural message. |
* |
* @param string $singular |
* @param string $plural |
* @param int $number |
* @param string $textDomain |
* @param string|null $locale |
* @return string |
* @throws Exception\OutOfBoundsException |
*/ |
public function translatePlural( |
$singular, |
$plural, |
$number, |
$textDomain = 'default', |
$locale = null |
) { |
$locale = $locale ?: $this->getLocale(); |
$translation = $this->getTranslatedMessage($singular, $locale, $textDomain); |
if ($translation === null || $translation === '') { |
if (null !== ($fallbackLocale = $this->getFallbackLocale()) |
&& $locale !== $fallbackLocale |
) { |
return $this->translatePlural( |
$singular, |
$plural, |
$number, |
$textDomain, |
$fallbackLocale |
); |
} |
return ($number == 1 ? $singular : $plural); |
} |
$index = $this->messages[$textDomain][$locale] |
->getPluralRule() |
->evaluate($number); |
if (!isset($translation[$index])) { |
throw new Exception\OutOfBoundsException(sprintf( |
'Provided index %d does not exist in plural array', $index |
)); |
} |
return $translation[$index]; |
} |
/** |
* Get a translated message. |
* |
* @param string $message |
* @param string $locale |
* @param string $textDomain |
* @return string|null |
*/ |
protected function getTranslatedMessage( |
$message, |
$locale = null, |
$textDomain = 'default' |
) { |
if ($message === '') { |
return ''; |
} |
if (!isset($this->messages[$textDomain][$locale])) { |
$this->loadMessages($textDomain, $locale); |
} |
if (!isset($this->messages[$textDomain][$locale][$message])) { |
return null; |
} |
return $this->messages[$textDomain][$locale][$message]; |
} |
/** |
* Add a translation file. |
* |
* @param string $type |
* @param string $filename |
* @param string $textDomain |
* @param string $locale |
* @return Translator |
*/ |
public function addTranslationFile( |
$type, |
$filename, |
$textDomain = 'default', |
$locale = null |
) { |
$locale = $locale ?: '*'; |
if (!isset($this->files[$textDomain])) { |
$this->files[$textDomain] = array(); |
} |
$this->files[$textDomain][$locale][] = array( |
'type' => $type, |
'filename' => $filename, |
); |
return $this; |
} |
/** |
* Add multiple translations with a file pattern. |
* |
* @param string $type |
* @param string $baseDir |
* @param string $pattern |
* @param string $textDomain |
* @return Translator |
*/ |
public function addTranslationFilePattern( |
$type, |
$baseDir, |
$pattern, |
$textDomain = 'default' |
) { |
if (!isset($this->patterns[$textDomain])) { |
$this->patterns[$textDomain] = array(); |
} |
$this->patterns[$textDomain][] = array( |
'type' => $type, |
'baseDir' => rtrim($baseDir, '/'), |
'pattern' => $pattern, |
); |
return $this; |
} |
/** |
* Add remote translations. |
* |
* @param string $type |
* @param string $textDomain |
* @return Translator |
*/ |
public function addRemoteTranslations($type, $textDomain = 'default') |
{ |
if (!isset($this->remote[$textDomain])) { |
$this->remote[$textDomain] = array(); |
} |
$this->remote[$textDomain][] = $type; |
return $this; |
} |
/** |
* Load messages for a given language and domain. |
* |
* @param string $textDomain |
* @param string $locale |
* @throws Exception\RuntimeException |
* @return void |
*/ |
protected function loadMessages($textDomain, $locale) |
{ |
if (!isset($this->messages[$textDomain])) { |
$this->messages[$textDomain] = array(); |
} |
if (null !== ($cache = $this->getCache())) { |
$cacheId = 'Zend_I18n_Translator_Messages_' . md5($textDomain . $locale); |
if (null !== ($result = $cache->getItem($cacheId))) { |
$this->messages[$textDomain][$locale] = $result; |
return; |
} |
} |
$hasToCache = false; |
// Try to load from remote sources |
if (isset($this->remote[$textDomain])) { |
foreach ($this->remote[$textDomain] as $loaderType) { |
$loader = $this->getPluginManager()->get($loaderType); |
if (!$loader instanceof RemoteLoaderInterface) { |
throw new Exception\RuntimeException('Specified loader is not a remote loader'); |
} |
if (isset($this->messages[$textDomain][$locale])) { |
$this->messages[$textDomain][$locale]->exchangeArray(array_merge( |
(array) $this->messages[$textDomain][$locale], |
(array) $loader->load($locale, $textDomain) |
)); |
} else { |
$this->messages[$textDomain][$locale] = $loader->load($locale, $textDomain); |
} |
$hasToCache = true; |
} |
} |
// Try to load from pattern |
if (isset($this->patterns[$textDomain])) { |
foreach ($this->patterns[$textDomain] as $pattern) { |
$filename = $pattern['baseDir'] . '/' . sprintf($pattern['pattern'], $locale); |
if (is_file($filename)) { |
$loader = $this->getPluginManager()->get($pattern['type']); |
if (!$loader instanceof FileLoaderInterface) { |
throw new Exception\RuntimeException('Specified loader is not a file loader'); |
} |
if (isset($this->messages[$textDomain][$locale])) { |
$this->messages[$textDomain][$locale]->exchangeArray(array_merge( |
(array) $this->messages[$textDomain][$locale], |
(array) $loader->load($locale, $filename) |
)); |
} else { |
$this->messages[$textDomain][$locale] = $loader->load($locale, $filename); |
} |
$hasToCache = true; |
} |
} |
} |
// Try to load from concrete files |
foreach (array($locale, '*') as $currentLocale) { |
if (!isset($this->files[$textDomain][$currentLocale])) { |
continue; |
} |
foreach ($this->files[$textDomain][$currentLocale] as $file) { |
$loader = $this->getPluginManager()->get($file['type']); |
if (!$loader instanceof FileLoaderInterface) { |
throw new Exception\RuntimeException('Specified loader is not a file loader'); |
} |
if (isset($this->messages[$textDomain][$locale])) { |
$this->messages[$textDomain][$locale]->exchangeArray(array_merge( |
(array) $this->messages[$textDomain][$locale], |
(array) $loader->load($locale, $file['filename']) |
)); |
} else { |
$this->messages[$textDomain][$locale] = $loader->load($locale, $file['filename']); |
} |
$hasToCache = true; |
} |
unset($this->files[$textDomain][$currentLocale]); |
} |
// Cache the loaded text domain |
if ($hasToCache && $cache !== null) { |
$cache->setItem($cacheId, $this->messages[$textDomain][$locale]); |
} |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/TextDomain.php |
---|
0,0 → 1,54 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\I18n\Translator; |
use ArrayObject; |
use Zend\I18n\Translator\Plural\Rule as PluralRule; |
/** |
* Text domain. |
*/ |
class TextDomain extends ArrayObject |
{ |
/** |
* Plural rule. |
* |
* @var PluralRule |
*/ |
protected $pluralRule; |
/** |
* Set the plural rule |
* |
* @param PluralRule $rule |
* @return TextDomain |
*/ |
public function setPluralRule(PluralRule $rule) |
{ |
$this->pluralRule = $rule; |
return $this; |
} |
/** |
* Get the plural rule. |
* |
* Lazy loads a default rule if none already registered |
* |
* @return PluralRule |
*/ |
public function getPluralRule() |
{ |
if ($this->pluralRule === null) { |
$this->setPluralRule(PluralRule::fromString('nplurals=2; plural=n==1')); |
} |
return $this->pluralRule; |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/Gettext.php |
---|
0,0 → 1,188 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\I18n\Translator\Loader; |
use Zend\I18n\Exception; |
use Zend\I18n\Translator\Plural\Rule as PluralRule; |
use Zend\I18n\Translator\TextDomain; |
use Zend\Stdlib\ErrorHandler; |
/** |
* Gettext loader. |
*/ |
class Gettext implements FileLoaderInterface |
{ |
/** |
* Current file pointer. |
* |
* @var resource |
*/ |
protected $file; |
/** |
* Whether the current file is little endian. |
* |
* @var bool |
*/ |
protected $littleEndian; |
/** |
* load(): defined by FileLoaderInterface. |
* |
* @see FileLoaderInterface::load() |
* @param string $locale |
* @param string $filename |
* @return TextDomain |
* @throws Exception\InvalidArgumentException |
*/ |
public function load($locale, $filename) |
{ |
if (!is_file($filename) || !is_readable($filename)) { |
throw new Exception\InvalidArgumentException(sprintf( |
'Could not open file %s for reading', |
$filename |
)); |
} |
$textDomain = new TextDomain(); |
ErrorHandler::start(); |
$this->file = fopen($filename, 'rb'); |
$error = ErrorHandler::stop(); |
if (false === $this->file) { |
throw new Exception\InvalidArgumentException(sprintf( |
'Could not open file %s for reading', |
$filename |
), 0, $error); |
} |
// Verify magic number |
$magic = fread($this->file, 4); |
if ($magic == "\x95\x04\x12\xde") { |
$this->littleEndian = false; |
} elseif ($magic == "\xde\x12\x04\x95") { |
$this->littleEndian = true; |
} else { |
fclose($this->file); |
throw new Exception\InvalidArgumentException(sprintf( |
'%s is not a valid gettext file', |
$filename |
)); |
} |
// Verify major revision (only 0 and 1 supported) |
$majorRevision = ($this->readInteger() >> 16); |
if ($majorRevision !== 0 && $majorRevision !== 1) { |
fclose($this->file); |
throw new Exception\InvalidArgumentException(sprintf( |
'%s has an unknown major revision', |
$filename |
)); |
} |
// Gather main information |
$numStrings = $this->readInteger(); |
$originalStringTableOffset = $this->readInteger(); |
$translationStringTableOffset = $this->readInteger(); |
// Usually there follow size and offset of the hash table, but we have |
// no need for it, so we skip them. |
fseek($this->file, $originalStringTableOffset); |
$originalStringTable = $this->readIntegerList(2 * $numStrings); |
fseek($this->file, $translationStringTableOffset); |
$translationStringTable = $this->readIntegerList(2 * $numStrings); |
// Read in all translations |
for ($current = 0; $current < $numStrings; $current++) { |
$sizeKey = $current * 2 + 1; |
$offsetKey = $current * 2 + 2; |
$originalStringSize = $originalStringTable[$sizeKey]; |
$originalStringOffset = $originalStringTable[$offsetKey]; |
$translationStringSize = $translationStringTable[$sizeKey]; |
$translationStringOffset = $translationStringTable[$offsetKey]; |
$originalString = array(''); |
if ($originalStringSize > 0) { |
fseek($this->file, $originalStringOffset); |
$originalString = explode("\0", fread($this->file, $originalStringSize)); |
} |
if ($translationStringSize > 0) { |
fseek($this->file, $translationStringOffset); |
$translationString = explode("\0", fread($this->file, $translationStringSize)); |
if (count($originalString) > 1 && count($translationString) > 1) { |
$textDomain[$originalString[0]] = $translationString; |
array_shift($originalString); |
foreach ($originalString as $string) { |
$textDomain[$string] = ''; |
} |
} else { |
$textDomain[$originalString[0]] = $translationString[0]; |
} |
} |
} |
// Read header entries |
if (array_key_exists('', $textDomain)) { |
$rawHeaders = explode("\n", trim($textDomain[''])); |
foreach ($rawHeaders as $rawHeader) { |
list($header, $content) = explode(':', $rawHeader, 2); |
if (trim(strtolower($header)) === 'plural-forms') { |
$textDomain->setPluralRule(PluralRule::fromString($content)); |
} |
} |
unset($textDomain['']); |
} |
fclose($this->file); |
return $textDomain; |
} |
/** |
* Read a single integer from the current file. |
* |
* @return integer |
*/ |
protected function readInteger() |
{ |
if ($this->littleEndian) { |
$result = unpack('Vint', fread($this->file, 4)); |
} else { |
$result = unpack('Nint', fread($this->file, 4)); |
} |
return $result['int']; |
} |
/** |
* Read an integer from the current file. |
* |
* @param integer $num |
* @return integer |
*/ |
protected function readIntegerList($num) |
{ |
if ($this->littleEndian) { |
return unpack('V' . $num, fread($this->file, 4 * $num)); |
} |
return unpack('N' . $num, fread($this->file, 4 * $num)); |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/FileLoaderInterface.php |
---|
0,0 → 1,25 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\I18n\Translator\Loader; |
/** |
* File loader interface. |
*/ |
interface FileLoaderInterface |
{ |
/** |
* Load translations from a file. |
* |
* @param string $locale |
* @param string $filename |
* @return \Zend\I18n\Translator\TextDomain|null |
*/ |
public function load($locale, $filename); |
} |
/trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/LoaderPluginManager.php |
---|
0,0 → 1,58 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\I18n\Translator; |
use Zend\I18n\Exception; |
use Zend\ServiceManager\AbstractPluginManager; |
/** |
* Plugin manager implementation for translation loaders. |
* |
* Enforces that loaders retrieved are either instances of |
* Loader\FileLoaderInterface or Loader\RemoteLoaderInterface. Additionally, |
* it registers a number of default loaders. |
*/ |
class LoaderPluginManager extends AbstractPluginManager |
{ |
/** |
* Default set of loaders. |
* |
* @var array |
*/ |
protected $invokableClasses = array( |
'gettext' => 'Zend\I18n\Translator\Loader\Gettext', |
'ini' => 'Zend\I18n\Translator\Loader\Ini', |
'phparray' => 'Zend\I18n\Translator\Loader\PhpArray', |
); |
/** |
* Validate the plugin. |
* |
* Checks that the filter loaded is an instance of |
* Loader\FileLoaderInterface or Loader\RemoteLoaderInterface. |
* |
* @param mixed $plugin |
* @return void |
* @throws Exception\RuntimeException if invalid |
*/ |
public function validatePlugin($plugin) |
{ |
if ($plugin instanceof Loader\FileLoaderInterface || $plugin instanceof Loader\RemoteLoaderInterface) { |
// we're okay |
return; |
} |
throw new Exception\RuntimeException(sprintf( |
'Plugin of type %s is invalid; must implement %s\Loader\FileLoaderInterface or %s\Loader\RemoteLoaderInterface', |
(is_object($plugin) ? get_class($plugin) : gettype($plugin)), |
__NAMESPACE__ |
)); |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/Stdlib/ErrorHandler.php |
---|
0,0 → 1,115 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\Stdlib; |
use ErrorException; |
/** |
* ErrorHandler that can be used to catch internal PHP errors |
* and convert to a ErrorException instance. |
*/ |
abstract class ErrorHandler |
{ |
/** |
* Active stack |
* |
* @var array |
*/ |
protected static $stack = array(); |
/** |
* Check if this error handler is active |
* |
* @return boolean |
*/ |
public static function started() |
{ |
return (bool) static::getNestedLevel(); |
} |
/** |
* Get the current nested level |
* |
* @return int |
*/ |
public static function getNestedLevel() |
{ |
return count(static::$stack); |
} |
/** |
* Starting the error handler |
* |
* @param int $errorLevel |
*/ |
public static function start($errorLevel = \E_WARNING) |
{ |
if (!static::$stack) { |
set_error_handler(array(get_called_class(), 'addError'), $errorLevel); |
} |
static::$stack[] = null; |
} |
/** |
* Stopping the error handler |
* |
* @param bool $throw Throw the ErrorException if any |
* @return null|ErrorException |
* @throws ErrorException If an error has been catched and $throw is true |
*/ |
public static function stop($throw = false) |
{ |
$errorException = null; |
if (static::$stack) { |
$errorException = array_pop(static::$stack); |
if (!static::$stack) { |
restore_error_handler(); |
} |
if ($errorException && $throw) { |
throw $errorException; |
} |
} |
return $errorException; |
} |
/** |
* Stop all active handler |
* |
* @return void |
*/ |
public static function clean() |
{ |
if (static::$stack) { |
restore_error_handler(); |
} |
static::$stack = array(); |
} |
/** |
* Add an error to the stack |
* |
* @param int $errno |
* @param string $errstr |
* @param string $errfile |
* @param int $errline |
* @return void |
*/ |
public static function addError($errno, $errstr = '', $errfile = '', $errline = 0) |
{ |
$stack = & static::$stack[ count(static::$stack) - 1 ]; |
$stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack); |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceManager.php |
---|
0,0 → 1,986 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\ServiceManager; |
use ReflectionClass; |
class ServiceManager implements ServiceLocatorInterface |
{ |
/**@#+ |
* Constants |
*/ |
const SCOPE_PARENT = 'parent'; |
const SCOPE_CHILD = 'child'; |
/**@#-*/ |
/** |
* Lookup for canonicalized names. |
* |
* @var array |
*/ |
protected $canonicalNames = array(); |
/** |
* @var bool |
*/ |
protected $allowOverride = false; |
/** |
* @var array |
*/ |
protected $invokableClasses = array(); |
/** |
* @var string|callable|\Closure|FactoryInterface[] |
*/ |
protected $factories = array(); |
/** |
* @var AbstractFactoryInterface[] |
*/ |
protected $abstractFactories = array(); |
/** |
* @var array |
*/ |
protected $pendingAbstractFactoryRequests = array(); |
/** |
* @var array |
*/ |
protected $shared = array(); |
/** |
* Registered services and cached values |
* |
* @var array |
*/ |
protected $instances = array(); |
/** |
* @var array |
*/ |
protected $aliases = array(); |
/** |
* @var array |
*/ |
protected $initializers = array(); |
/** |
* @var ServiceManager[] |
*/ |
protected $peeringServiceManagers = array(); |
/** |
* Whether or not to share by default |
* |
* @var bool |
*/ |
protected $shareByDefault = true; |
/** |
* @var bool |
*/ |
protected $retrieveFromPeeringManagerFirst = false; |
/** |
* @var bool Track whether not to throw exceptions during create() |
*/ |
protected $throwExceptionInCreate = true; |
/** |
* @var array map of characters to be replaced through strtr |
*/ |
protected $canonicalNamesReplacements = array('-' => '', '_' => '', ' ' => '', '\\' => '', '/' => ''); |
/** |
* Constructor |
* |
* @param ConfigInterface $config |
*/ |
public function __construct(ConfigInterface $config = null) |
{ |
if ($config) { |
$config->configureServiceManager($this); |
} |
} |
/** |
* Set allow override |
* |
* @param $allowOverride |
* @return ServiceManager |
*/ |
public function setAllowOverride($allowOverride) |
{ |
$this->allowOverride = (bool) $allowOverride; |
return $this; |
} |
/** |
* Get allow override |
* |
* @return bool |
*/ |
public function getAllowOverride() |
{ |
return $this->allowOverride; |
} |
/** |
* Set flag indicating whether services are shared by default |
* |
* @param bool $shareByDefault |
* @return ServiceManager |
* @throws Exception\RuntimeException if allowOverride is false |
*/ |
public function setShareByDefault($shareByDefault) |
{ |
if ($this->allowOverride === false) { |
throw new Exception\RuntimeException(sprintf( |
'%s: cannot alter default shared service setting; container is marked immutable (allow_override is false)', |
__METHOD__ |
)); |
} |
$this->shareByDefault = (bool) $shareByDefault; |
return $this; |
} |
/** |
* Are services shared by default? |
* |
* @return bool |
*/ |
public function shareByDefault() |
{ |
return $this->shareByDefault; |
} |
/** |
* Set throw exceptions in create |
* |
* @param bool $throwExceptionInCreate |
* @return ServiceManager |
*/ |
public function setThrowExceptionInCreate($throwExceptionInCreate) |
{ |
$this->throwExceptionInCreate = $throwExceptionInCreate; |
return $this; |
} |
/** |
* Get throw exceptions in create |
* |
* @return bool |
*/ |
public function getThrowExceptionInCreate() |
{ |
return $this->throwExceptionInCreate; |
} |
/** |
* Set flag indicating whether to pull from peering manager before attempting creation |
* |
* @param bool $retrieveFromPeeringManagerFirst |
* @return ServiceManager |
*/ |
public function setRetrieveFromPeeringManagerFirst($retrieveFromPeeringManagerFirst = true) |
{ |
$this->retrieveFromPeeringManagerFirst = (bool) $retrieveFromPeeringManagerFirst; |
return $this; |
} |
/** |
* Should we retrieve from the peering manager prior to attempting to create a service? |
* |
* @return bool |
*/ |
public function retrieveFromPeeringManagerFirst() |
{ |
return $this->retrieveFromPeeringManagerFirst; |
} |
/** |
* Set invokable class |
* |
* @param string $name |
* @param string $invokableClass |
* @param bool $shared |
* @return ServiceManager |
* @throws Exception\InvalidServiceNameException |
*/ |
public function setInvokableClass($name, $invokableClass, $shared = null) |
{ |
$cName = $this->canonicalizeName($name); |
if ($this->has(array($cName, $name), false)) { |
if ($this->allowOverride === false) { |
throw new Exception\InvalidServiceNameException(sprintf( |
'A service by the name or alias "%s" already exists and cannot be overridden; please use an alternate name', |
$cName |
)); |
} |
$this->unregisterService($cName); |
} |
if ($shared === null) { |
$shared = $this->shareByDefault(); |
} |
$this->invokableClasses[$cName] = $invokableClass; |
$this->shared[$cName] = (bool) $shared; |
return $this; |
} |
/** |
* Set factory |
* |
* @param string $name |
* @param string|FactoryInterface|callable $factory |
* @param bool $shared |
* @return ServiceManager |
* @throws Exception\InvalidArgumentException |
* @throws Exception\InvalidServiceNameException |
*/ |
public function setFactory($name, $factory, $shared = null) |
{ |
$cName = $this->canonicalizeName($name); |
if (!is_string($factory) && !$factory instanceof FactoryInterface && !is_callable($factory)) { |
throw new Exception\InvalidArgumentException( |
'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
); |
} |
if ($this->has(array($cName, $name), false)) { |
if ($this->allowOverride === false) { |
throw new Exception\InvalidServiceNameException(sprintf( |
'A service by the name or alias "%s" already exists and cannot be overridden, please use an alternate name', |
$cName |
)); |
} |
$this->unregisterService($cName); |
} |
if ($shared === null) { |
$shared = $this->shareByDefault(); |
} |
$this->factories[$cName] = $factory; |
$this->shared[$cName] = (bool) $shared; |
return $this; |
} |
/** |
* Add abstract factory |
* |
* @param AbstractFactoryInterface|string $factory |
* @param bool $topOfStack |
* @return ServiceManager |
* @throws Exception\InvalidArgumentException if the abstract factory is invalid |
*/ |
public function addAbstractFactory($factory, $topOfStack = true) |
{ |
if (!is_string($factory) && !$factory instanceof AbstractFactoryInterface) { |
throw new Exception\InvalidArgumentException( |
'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
); |
} |
if (is_string($factory)) { |
if (!class_exists($factory, true)) { |
throw new Exception\InvalidArgumentException( |
'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
); |
} |
$refl = new ReflectionClass($factory); |
if (!$refl->implementsInterface(__NAMESPACE__ . '\\AbstractFactoryInterface')) { |
throw new Exception\InvalidArgumentException( |
'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
); |
} |
} |
if ($topOfStack) { |
array_unshift($this->abstractFactories, $factory); |
} else { |
array_push($this->abstractFactories, $factory); |
} |
return $this; |
} |
/** |
* Add initializer |
* |
* @param callable|InitializerInterface $initializer |
* @param bool $topOfStack |
* @return ServiceManager |
* @throws Exception\InvalidArgumentException |
*/ |
public function addInitializer($initializer, $topOfStack = true) |
{ |
if (!is_callable($initializer) && !$initializer instanceof InitializerInterface) { |
if (!is_string($initializer) |
|| !$this->isSubclassOf($initializer, __NAMESPACE__ . '\InitializerInterface') |
) { |
throw new Exception\InvalidArgumentException('$initializer should be callable.'); |
} |
$initializer = new $initializer; |
} |
if ($topOfStack) { |
array_unshift($this->initializers, $initializer); |
} else { |
array_push($this->initializers, $initializer); |
} |
return $this; |
} |
/** |
* Register a service with the locator |
* |
* @param string $name |
* @param mixed $service |
* @return ServiceManager |
* @throws Exception\InvalidServiceNameException |
*/ |
public function setService($name, $service) |
{ |
$cName = $this->canonicalizeName($name); |
if ($this->has($cName, false)) { |
if ($this->allowOverride === false) { |
throw new Exception\InvalidServiceNameException(sprintf( |
'%s: A service by the name "%s" or alias already exists and cannot be overridden, please use an alternate name.', |
__METHOD__, |
$name |
)); |
} |
$this->unregisterService($cName); |
} |
$this->instances[$cName] = $service; |
return $this; |
} |
/** |
* @param string $name |
* @param bool $isShared |
* @return ServiceManager |
* @throws Exception\ServiceNotFoundException |
*/ |
public function setShared($name, $isShared) |
{ |
$cName = $this->canonicalizeName($name); |
if ( |
!isset($this->invokableClasses[$cName]) |
&& !isset($this->factories[$cName]) |
&& !$this->canCreateFromAbstractFactory($cName, $name) |
) { |
throw new Exception\ServiceNotFoundException(sprintf( |
'%s: A service by the name "%s" was not found and could not be marked as shared', |
__METHOD__, |
$name |
)); |
} |
$this->shared[$cName] = (bool) $isShared; |
return $this; |
} |
/** |
* Retrieve a registered instance |
* |
* @param string $name |
* @param bool $usePeeringServiceManagers |
* @throws Exception\ServiceNotFoundException |
* @return object|array |
*/ |
public function get($name, $usePeeringServiceManagers = true) |
{ |
$cName = $this->canonicalizeName($name); |
$isAlias = false; |
if ($this->hasAlias($cName)) { |
$isAlias = true; |
do { |
$cName = $this->aliases[$cName]; |
} while ($this->hasAlias($cName)); |
} |
$instance = null; |
$retrieveFromPeeringManagerFirst = $this->retrieveFromPeeringManagerFirst(); |
if ($usePeeringServiceManagers && $retrieveFromPeeringManagerFirst) { |
$instance = $this->retrieveFromPeeringManager($name); |
if(null !== $instance) { |
return $instance; |
} |
} |
if (isset($this->instances[$cName])) { |
return $this->instances[$cName]; |
} |
if (!$instance) { |
if ($this->canCreate(array($cName, $name))) { |
$instance = $this->create(array($cName, $name)); |
} elseif ($usePeeringServiceManagers && !$retrieveFromPeeringManagerFirst) { |
$instance = $this->retrieveFromPeeringManager($name); |
} |
} |
// Still no instance? raise an exception |
if ($instance === null && !is_array($instance)) { |
if ($isAlias) { |
throw new Exception\ServiceNotFoundException(sprintf( |
'An alias "%s" was requested but no service could be found.', |
$name |
)); |
} |
throw new Exception\ServiceNotFoundException(sprintf( |
'%s was unable to fetch or create an instance for %s', |
__METHOD__, |
$name |
)); |
} |
if ( |
($this->shareByDefault() && !isset($this->shared[$cName])) |
|| (isset($this->shared[$cName]) && $this->shared[$cName] === true) |
) { |
$this->instances[$cName] = $instance; |
} |
return $instance; |
} |
/** |
* Create an instance |
* |
* @param string|array $name |
* @return bool|object |
* @throws Exception\ServiceNotFoundException |
* @throws Exception\ServiceNotCreatedException |
*/ |
public function create($name) |
{ |
$instance = false; |
if (is_array($name)) { |
list($cName, $rName) = $name; |
} else { |
$rName = $name; |
$cName = $this->canonicalizeName($rName); |
} |
if (isset($this->factories[$cName])) { |
$instance = $this->createFromFactory($cName, $rName); |
} |
if ($instance === false && isset($this->invokableClasses[$cName])) { |
$instance = $this->createFromInvokable($cName, $rName); |
} |
if ($instance === false && $this->canCreateFromAbstractFactory($cName, $rName)) { |
$instance = $this->createFromAbstractFactory($cName, $rName); |
} |
if ($this->throwExceptionInCreate == true && $instance === false) { |
throw new Exception\ServiceNotFoundException(sprintf( |
'No valid instance was found for %s%s', |
$cName, |
($rName ? '(alias: ' . $rName . ')' : '') |
)); |
} |
foreach ($this->initializers as $initializer) { |
if ($initializer instanceof InitializerInterface) { |
$initializer->initialize($instance, $this); |
} elseif (is_object($initializer) && is_callable($initializer)) { |
$initializer($instance, $this); |
} else { |
call_user_func($initializer, $instance, $this); |
} |
} |
return $instance; |
} |
/** |
* Determine if we can create an instance. |
* |
* @param string|array $name |
* @param bool $checkAbstractFactories |
* @return bool |
*/ |
public function canCreate($name, $checkAbstractFactories = true) |
{ |
if (is_array($name)) { |
list($cName, $rName) = $name; |
} else { |
$rName = $name; |
$cName = $this->canonicalizeName($rName); |
} |
if ( |
isset($this->invokableClasses[$cName]) |
|| isset($this->factories[$cName]) |
|| isset($this->aliases[$cName]) |
|| isset($this->instances[$cName]) |
) { |
return true; |
} |
if ($checkAbstractFactories && $this->canCreateFromAbstractFactory($cName, $rName)) { |
return true; |
} |
return false; |
} |
/** |
* @param string|array $name |
* @param bool $checkAbstractFactories |
* @param bool $usePeeringServiceManagers |
* @return bool |
*/ |
public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true) |
{ |
if (is_array($name)) { |
list($cName, $rName) = $name; |
} else { |
$rName = $name; |
$cName = $this->canonicalizeName($rName); |
} |
if ($this->canCreate(array($cName, $rName), $checkAbstractFactories)) { |
return true; |
} |
if ($usePeeringServiceManagers) { |
foreach ($this->peeringServiceManagers as $peeringServiceManager) { |
if ($peeringServiceManager->has($rName)) { |
return true; |
} |
} |
} |
return false; |
} |
/** |
* Determine if we can create an instance from an abstract factory. |
* |
* @param string $cName |
* @param string $rName |
* @return bool |
*/ |
public function canCreateFromAbstractFactory($cName, $rName) |
{ |
// check abstract factories |
foreach ($this->abstractFactories as $index => $abstractFactory) { |
// Support string abstract factory class names |
if (is_string($abstractFactory) && class_exists($abstractFactory, true)) { |
$this->abstractFactories[$index] = $abstractFactory = new $abstractFactory(); |
} |
if ( |
isset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]) |
&& $this->pendingAbstractFactoryRequests[get_class($abstractFactory)] == $rName |
) { |
return false; |
} |
if ($abstractFactory->canCreateServiceWithName($this, $cName, $rName)) { |
return true; |
} |
} |
return false; |
} |
/** |
* @param string $alias |
* @param string $nameOrAlias |
* @return ServiceManager |
* @throws Exception\ServiceNotFoundException |
* @throws Exception\InvalidServiceNameException |
*/ |
public function setAlias($alias, $nameOrAlias) |
{ |
if (!is_string($alias) || !is_string($nameOrAlias)) { |
throw new Exception\InvalidServiceNameException('Service or alias names must be strings.'); |
} |
$cAlias = $this->canonicalizeName($alias); |
$nameOrAlias = $this->canonicalizeName($nameOrAlias); |
if ($alias == '' || $nameOrAlias == '') { |
throw new Exception\InvalidServiceNameException('Invalid service name alias'); |
} |
if ($this->allowOverride === false && $this->has(array($cAlias, $alias), false)) { |
throw new Exception\InvalidServiceNameException(sprintf( |
'An alias by the name "%s" or "%s" already exists', |
$cAlias, |
$alias |
)); |
} |
$this->aliases[$cAlias] = $nameOrAlias; |
return $this; |
} |
/** |
* Determine if we have an alias |
* |
* @param string $alias |
* @return bool |
*/ |
public function hasAlias($alias) |
{ |
$alias = $this->canonicalizeName($alias); |
return (isset($this->aliases[$alias])); |
} |
/** |
* Create scoped service manager |
* |
* @param string $peering |
* @return ServiceManager |
*/ |
public function createScopedServiceManager($peering = self::SCOPE_PARENT) |
{ |
$scopedServiceManager = new ServiceManager(); |
if ($peering == self::SCOPE_PARENT) { |
$scopedServiceManager->peeringServiceManagers[] = $this; |
} |
if ($peering == self::SCOPE_CHILD) { |
$this->peeringServiceManagers[] = $scopedServiceManager; |
} |
return $scopedServiceManager; |
} |
/** |
* Add a peering relationship |
* |
* @param ServiceManager $manager |
* @param string $peering |
* @return ServiceManager |
*/ |
public function addPeeringServiceManager(ServiceManager $manager, $peering = self::SCOPE_PARENT) |
{ |
if ($peering == self::SCOPE_PARENT) { |
$this->peeringServiceManagers[] = $manager; |
} |
if ($peering == self::SCOPE_CHILD) { |
$manager->peeringServiceManagers[] = $this; |
} |
return $this; |
} |
/** |
* Canonicalize name |
* |
* @param string $name |
* @return string |
*/ |
protected function canonicalizeName($name) |
{ |
if (isset($this->canonicalNames[$name])) { |
return $this->canonicalNames[$name]; |
} |
// this is just for performance instead of using str_replace |
return $this->canonicalNames[$name] = strtolower(strtr($name, $this->canonicalNamesReplacements)); |
} |
/** |
* Create service via callback |
* |
* @param callable $callable |
* @param string $cName |
* @param string $rName |
* @throws Exception\ServiceNotCreatedException |
* @throws Exception\ServiceNotFoundException |
* @throws Exception\CircularDependencyFoundException |
* @return object |
*/ |
protected function createServiceViaCallback($callable, $cName, $rName) |
{ |
static $circularDependencyResolver = array(); |
$depKey = spl_object_hash($this) . '-' . $cName; |
if (isset($circularDependencyResolver[$depKey])) { |
$circularDependencyResolver = array(); |
throw new Exception\CircularDependencyFoundException('Circular dependency for LazyServiceLoader was found for instance ' . $rName); |
} |
try { |
$circularDependencyResolver[$depKey] = true; |
$instance = call_user_func($callable, $this, $cName, $rName); |
unset($circularDependencyResolver[$depKey]); |
} catch (Exception\ServiceNotFoundException $e) { |
unset($circularDependencyResolver[$depKey]); |
throw $e; |
} catch (\Exception $e) { |
unset($circularDependencyResolver[$depKey]); |
throw new Exception\ServiceNotCreatedException( |
sprintf('An exception was raised while creating "%s"; no instance returned', $rName), |
$e->getCode(), |
$e |
); |
} |
if ($instance === null) { |
throw new Exception\ServiceNotCreatedException('The factory was called but did not return an instance.'); |
} |
return $instance; |
} |
/** |
* Retrieve a keyed list of all registered services. Handy for debugging! |
* |
* @return array |
*/ |
public function getRegisteredServices() |
{ |
return array( |
'invokableClasses' => array_keys($this->invokableClasses), |
'factories' => array_keys($this->factories), |
'aliases' => array_keys($this->aliases), |
'instances' => array_keys($this->instances), |
); |
} |
/** |
* Retrieve a keyed list of all canonical names. Handy for debugging! |
* |
* @return array |
*/ |
public function getCanonicalNames() |
{ |
return $this->canonicalNames; |
} |
/** |
* Allows to override the canonical names lookup map with predefined |
* values. |
* |
* @param array $canonicalNames |
* @return ServiceManager |
*/ |
public function setCanonicalNames($canonicalNames) |
{ |
$this->canonicalNames = $canonicalNames; |
return $this; |
} |
/** |
* Attempt to retrieve an instance via a peering manager |
* |
* @param string $name |
* @return mixed |
*/ |
protected function retrieveFromPeeringManager($name) |
{ |
foreach ($this->peeringServiceManagers as $peeringServiceManager) { |
if ($peeringServiceManager->has($name)) { |
return $peeringServiceManager->get($name); |
} |
} |
$name = $this->canonicalizeName($name); |
if ($this->hasAlias($name)) { |
do { |
$name = $this->aliases[$name]; |
} while ($this->hasAlias($name)); |
} |
foreach ($this->peeringServiceManagers as $peeringServiceManager) { |
if ($peeringServiceManager->has($name)) { |
return $peeringServiceManager->get($name); |
} |
} |
return null; |
} |
/** |
* Attempt to create an instance via an invokable class |
* |
* @param string $canonicalName |
* @param string $requestedName |
* @return null|\stdClass |
* @throws Exception\ServiceNotFoundException If resolved class does not exist |
*/ |
protected function createFromInvokable($canonicalName, $requestedName) |
{ |
$invokable = $this->invokableClasses[$canonicalName]; |
if (!class_exists($invokable)) { |
throw new Exception\ServiceNotFoundException(sprintf( |
'%s: failed retrieving "%s%s" via invokable class "%s"; class does not exist', |
__METHOD__, |
$canonicalName, |
($requestedName ? '(alias: ' . $requestedName . ')' : ''), |
$invokable |
)); |
} |
$instance = new $invokable; |
return $instance; |
} |
/** |
* Attempt to create an instance via a factory |
* |
* @param string $canonicalName |
* @param string $requestedName |
* @return mixed |
* @throws Exception\ServiceNotCreatedException If factory is not callable |
*/ |
protected function createFromFactory($canonicalName, $requestedName) |
{ |
$factory = $this->factories[$canonicalName]; |
if (is_string($factory) && class_exists($factory, true)) { |
$factory = new $factory; |
$this->factories[$canonicalName] = $factory; |
} |
if ($factory instanceof FactoryInterface) { |
$instance = $this->createServiceViaCallback(array($factory, 'createService'), $canonicalName, $requestedName); |
} elseif (is_callable($factory)) { |
$instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName); |
} else { |
throw new Exception\ServiceNotCreatedException(sprintf( |
'While attempting to create %s%s an invalid factory was registered for this instance type.', |
$canonicalName, |
($requestedName ? '(alias: ' . $requestedName . ')' : '') |
)); |
} |
return $instance; |
} |
/** |
* Attempt to create an instance via an abstract factory |
* |
* @param string $canonicalName |
* @param string $requestedName |
* @return object|null |
* @throws Exception\ServiceNotCreatedException If abstract factory is not callable |
*/ |
protected function createFromAbstractFactory($canonicalName, $requestedName) |
{ |
foreach ($this->abstractFactories as $index => $abstractFactory) { |
// support factories as strings |
if (is_string($abstractFactory) && class_exists($abstractFactory, true)) { |
$this->abstractFactories[$index] = $abstractFactory = new $abstractFactory; |
} elseif (!$abstractFactory instanceof AbstractFactoryInterface) { |
throw new Exception\ServiceNotCreatedException(sprintf( |
'While attempting to create %s%s an abstract factory could not produce a valid instance.', |
$canonicalName, |
($requestedName ? '(alias: ' . $requestedName . ')' : '') |
)); |
} |
try { |
if ($abstractFactory->canCreateServiceWithName($this, $canonicalName, $requestedName)) { |
$this->pendingAbstractFactoryRequests[get_class($abstractFactory)] = $requestedName; |
$instance = $this->createServiceViaCallback( |
array($abstractFactory, 'createServiceWithName'), |
$canonicalName, |
$requestedName |
); |
unset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]); |
} else { |
$instance = false; |
} |
} catch (\Exception $e) { |
unset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]); |
throw new Exception\ServiceNotCreatedException( |
sprintf( |
'An abstract factory could not create an instance of %s%s.', |
$canonicalName, |
($requestedName ? '(alias: ' . $requestedName . ')' : '') |
), |
$e->getCode(), |
$e |
); |
} |
if (is_object($instance)) { |
break; |
} |
} |
return $instance; |
} |
/** |
* Checks if the object has this class as one of its parents |
* |
* @see https://bugs.php.net/bug.php?id=53727 |
* @see https://github.com/zendframework/zf2/pull/1807 |
* |
* @param string $className |
* @param string $type |
* @return bool |
*/ |
protected static function isSubclassOf($className, $type) |
{ |
if (is_subclass_of($className, $type)) { |
return true; |
} |
if (version_compare(PHP_VERSION, '5.3.7', '>=')) { |
return false; |
} |
if (!interface_exists($type)) { |
return false; |
} |
$r = new ReflectionClass($className); |
return $r->implementsInterface($type); |
} |
/** |
* Unregister a service |
* |
* Called when $allowOverride is true and we detect that a service being |
* added to the instance already exists. This will remove the duplicate |
* entry, and also any shared flags previously registered. |
* |
* @param string $canonical |
* @return void |
*/ |
protected function unregisterService($canonical) |
{ |
$types = array('invokableClasses', 'factories', 'aliases'); |
foreach ($types as $type) { |
if (isset($this->{$type}[$canonical])) { |
unset($this->{$type}[$canonical]); |
break; |
} |
} |
if (isset($this->instances[$canonical])) { |
unset($this->instances[$canonical]); |
} |
if (isset($this->shared[$canonical])) { |
unset($this->shared[$canonical]); |
} |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/AbstractPluginManager.php |
---|
0,0 → 1,214 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\ServiceManager; |
/** |
* ServiceManager implementation for managing plugins |
* |
* Automatically registers an initializer which should be used to verify that |
* a plugin instance is of a valid type. Additionally, allows plugins to accept |
* an array of options for the constructor, which can be used to configure |
* the plugin when retrieved. Finally, enables the allowOverride property by |
* default to allow registering factories, aliases, and invokables to take |
* the place of those provided by the implementing class. |
*/ |
abstract class AbstractPluginManager extends ServiceManager implements ServiceLocatorAwareInterface |
{ |
/** |
* Allow overriding by default |
* |
* @var bool |
*/ |
protected $allowOverride = true; |
/** |
* Whether or not to auto-add a class as an invokable class if it exists |
* |
* @var bool |
*/ |
protected $autoAddInvokableClass = true; |
/** |
* Options to use when creating an instance |
* |
* @var mixed |
*/ |
protected $creationOptions = null; |
/** |
* The main service locator |
* |
* @var ServiceLocatorInterface |
*/ |
protected $serviceLocator; |
/** |
* Constructor |
* |
* Add a default initializer to ensure the plugin is valid after instance |
* creation. |
* |
* @param null|ConfigInterface $configuration |
*/ |
public function __construct(ConfigInterface $configuration = null) |
{ |
parent::__construct($configuration); |
$self = $this; |
$this->addInitializer(function ($instance) use ($self) { |
if ($instance instanceof ServiceLocatorAwareInterface) { |
$instance->setServiceLocator($self); |
} |
}); |
} |
/** |
* Validate the plugin |
* |
* Checks that the filter loaded is either a valid callback or an instance |
* of FilterInterface. |
* |
* @param mixed $plugin |
* @return void |
* @throws Exception\RuntimeException if invalid |
*/ |
abstract public function validatePlugin($plugin); |
/** |
* Retrieve a service from the manager by name |
* |
* Allows passing an array of options to use when creating the instance. |
* createFromInvokable() will use these and pass them to the instance |
* constructor if not null and a non-empty array. |
* |
* @param string $name |
* @param array $options |
* @param bool $usePeeringServiceManagers |
* @return object |
*/ |
public function get($name, $options = array(), $usePeeringServiceManagers = true) |
{ |
// Allow specifying a class name directly; registers as an invokable class |
if (!$this->has($name) && $this->autoAddInvokableClass && class_exists($name)) { |
$this->setInvokableClass($name, $name); |
} |
$this->creationOptions = $options; |
$instance = parent::get($name, $usePeeringServiceManagers); |
$this->creationOptions = null; |
$this->validatePlugin($instance); |
return $instance; |
} |
/** |
* Register a service with the locator. |
* |
* Validates that the service object via validatePlugin() prior to |
* attempting to register it. |
* |
* @param string $name |
* @param mixed $service |
* @param bool $shared |
* @return AbstractPluginManager |
* @throws Exception\InvalidServiceNameException |
*/ |
public function setService($name, $service, $shared = true) |
{ |
if ($service) { |
$this->validatePlugin($service); |
} |
parent::setService($name, $service, $shared); |
return $this; |
} |
/** |
* Set the main service locator so factories can have access to it to pull deps |
* |
* @param ServiceLocatorInterface $serviceLocator |
* @return AbstractPluginManager |
*/ |
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) |
{ |
$this->serviceLocator = $serviceLocator; |
return $this; |
} |
/** |
* Get the main plugin manager. Useful for fetching dependencies from within factories. |
* |
* @return mixed |
*/ |
public function getServiceLocator() |
{ |
return $this->serviceLocator; |
} |
/** |
* Attempt to create an instance via an invokable class |
* |
* Overrides parent implementation by passing $creationOptions to the |
* constructor, if non-null. |
* |
* @param string $canonicalName |
* @param string $requestedName |
* @return null|\stdClass |
* @throws Exception\ServiceNotCreatedException If resolved class does not exist |
*/ |
protected function createFromInvokable($canonicalName, $requestedName) |
{ |
$invokable = $this->invokableClasses[$canonicalName]; |
if (null === $this->creationOptions |
|| (is_array($this->creationOptions) && empty($this->creationOptions)) |
) { |
$instance = new $invokable(); |
} else { |
$instance = new $invokable($this->creationOptions); |
} |
return $instance; |
} |
/** |
* Attempt to create an instance via a factory class |
* |
* Overrides parent implementation by passing $creationOptions to the |
* constructor, if non-null. |
* |
* @param string $canonicalName |
* @param string $requestedName |
* @return mixed |
* @throws Exception\ServiceNotCreatedException If factory is not callable |
*/ |
protected function createFromFactory($canonicalName, $requestedName) |
{ |
$factory = $this->factories[$canonicalName]; |
if (is_string($factory) && class_exists($factory, true)) { |
if (null === $this->creationOptions || (is_array($this->creationOptions) && empty($this->creationOptions))) { |
$factory = new $factory(); |
} else { |
$factory = new $factory($this->creationOptions); |
} |
$this->factories[$canonicalName] = $factory; |
} |
if ($factory instanceof FactoryInterface) { |
$instance = $this->createServiceViaCallback(array($factory, 'createService'), $canonicalName, $requestedName); |
} elseif (is_callable($factory)) { |
$instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName); |
} else { |
throw new Exception\ServiceNotCreatedException(sprintf( |
'While attempting to create %s%s an invalid factory was registered for this instance type.', $canonicalName, ($requestedName ? '(alias: ' . $requestedName . ')' : '') |
)); |
} |
return $instance; |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorAwareInterface.php |
---|
0,0 → 1,27 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\ServiceManager; |
interface ServiceLocatorAwareInterface |
{ |
/** |
* Set service locator |
* |
* @param ServiceLocatorInterface $serviceLocator |
*/ |
public function setServiceLocator(ServiceLocatorInterface $serviceLocator); |
/** |
* Get service locator |
* |
* @return ServiceLocatorInterface |
*/ |
public function getServiceLocator(); |
} |
/trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorInterface.php |
---|
0,0 → 1,33 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\ServiceManager; |
/** |
* Service locator interface |
*/ |
interface ServiceLocatorInterface |
{ |
/** |
* Retrieve a registered instance |
* |
* @param string $name |
* @throws Exception\ServiceNotFoundException |
* @return object|array |
*/ |
public function get($name); |
/** |
* Check for a registered instance |
* |
* @param string|array $name |
* @return bool |
*/ |
public function has($name); |
} |