Read the content of one class and return the result of the analysis.
Return FALSE if the cass found is not a valid service.

@param Smd $smd
@param string $classname
@return Service
965566
static public function read(Smd $smd, $classname)
    {
        $reflectedclass = new \ReflectionClass($classname);
        return self::isValid($smd, $reflectedclass) ? new Service($smd, $reflectedclass) : false;
    }
939493
public static function getByClassName($className)
    {
        $bind = self::getInstance()->bind;

        foreach ($bind as $abstract => $abstractClassName) {
            if ($className == $abstractClassName) {
                return self::get($abstract);
            }
        }

        throw new ContainerException('Cannot find "' . $className . '" in container');
    }
969143
public function hasClass($className)
    {
        if ($this->hasAttribute('class')) {
            return in_array($className, preg_split('/\s+/', $this->getAttribute('class')));
        }

        return false;
    }
574295
public function deserialize($jsonString, $className)
    {
        if (!$this->isValidClassName($className)) {
            throw new \Exception($className.' is not defined in OpenApi PHP Annotations');
        }
        return $this->doDeserialize(json_decode($jsonString), $className);
    }
917747
private static function classBelongsToNamespace($className, $namespaces)
    {
        $belongsToNamespace = false;
        $check = array_values($namespaces);
        $lcClassName = strtolower(ltrim($className, '\\'));

        foreach ($check as $namespace) {
            if (false !== strpos($lcClassName, strtolower($namespace))) {
                $belongsToNamespace = true;
                continue;
            }
        }
        return $belongsToNamespace;
    }
158347
public function getMetadataFor($className)
    {
        if (!$this->isService($className)) {
            throw Exception::classIsNotMappedService($className);
        }

        if (!isset($this->loadedMetadata[$className])) {
            $this->loadMetadata($className);
        }

        if ($this->cache) {
            $cacheId = $this->cachePrefix . $className . $this->cacheSuffix;

            if (($cached = $this->cache->fetch($cacheId)) !== false) {
                $this->loadedMetadata[$className] = $cached;
            } else {
                $this->loadMetadata($className);
                $this->cache->save($cacheId, $this->loadedMetadata[$className], null);
            }
        } else {
            $this->loadMetadata($className);
        }

        return $this->loadedMetadata[$className];
    }
88124
public static function parse_class_spec($classSpec)
    {
        if (isset(static::$_cache_parse[$classSpec])) {
            return static::$_cache_parse[$classSpec];
        }

        $tokens = token_get_all("<?php $classSpec");
        $class = null;
        $args = array();

        // Keep track of the current bucket that we're putting data into
        $bucket = &$args;
        $bucketStack = array();
        $hadNamespace = false;
        $currentKey = null;

        foreach ($tokens as $token) {
            // $forceResult used to allow null result to be detected
            $result = $forceResult = null;
            $tokenName = is_array($token) ? $token[0] : $token;

            // Get the class name
            if ($class === null && is_array($token) && $token[0] === T_STRING) {
                $class = $token[1];
            } elseif (is_array($token) && $token[0] === T_NS_SEPARATOR) {
                $class .= $token[1];
                $hadNamespace = true;
            } elseif ($token === '.') {
                // Treat service name separator as NS separator
                $class .= '.';
                $hadNamespace = true;
            } elseif ($hadNamespace && is_array($token) && $token[0] === T_STRING) {
                $class .= $token[1];
                $hadNamespace = false;
            // Get arguments
            } elseif (is_array($token)) {
                switch ($token[0]) {
                    case T_CONSTANT_ENCAPSED_STRING:
                        $argString = $token[1];
                        switch ($argString[0]) {
                            case '"':
                                $result = stripcslashes(substr($argString, 1, -1));
                                break;
                            case "'":
                                $result = str_replace(
                                    ["\\\\", "\\'"],
                                    ["\\", "'"],
                                    substr($argString, 1, -1)
                                );
                                break;
                            default:
                                throw new Exception("Bad T_CONSTANT_ENCAPSED_STRING arg $argString");
                        }

                        break;

                    case T_DNUMBER:
                        $result = (double)$token[1];
                        break;

                    case T_LNUMBER:
                        $result = (int)$token[1];
                        break;

                    case T_DOUBLE_ARROW:
                        // We've encountered an associative array (the array itself has already been
                        // added to the bucket), so the previous item added to the bucket is the key
                        end($bucket);
                        $currentKey = current($bucket);
                        array_pop($bucket);
                        break;

                    case T_STRING:
                        switch ($token[1]) {
                            case 'true':
                                $result = true;

                                break;
                            case 'false':
                                $result = false;

                                break;
                            case 'null':
                                $result = null;
                                $forceResult = true;

                                break;
                            default:
                                throw new Exception("Bad T_STRING arg '{$token[1]}'");
                        }

                        break;

                    case T_ARRAY:
                        $result = array();
                        break;
                }
            } else {
                if ($tokenName === '[') {
                    $result = array();
                } elseif (($tokenName === ')' || $tokenName === ']') && !empty($bucketStack)) {
                    // Store the bucket we're currently working on
                    $oldBucket = $bucket;
                    // Fetch the key for the bucket at the top of the stack
                    end($bucketStack);
                    $key = key($bucketStack);
                    reset($bucketStack);
                    // Re-instate the bucket from the top of the stack
                    $bucket = &$bucketStack[$key];
                    // Add our saved, "nested" bucket to the bucket we just popped off the stack
                    $bucket[$key] = $oldBucket;
                    // Remove the bucket we just popped off the stack
                    array_pop($bucketStack);
                }
            }

            // If we've got something to add to the bucket, add it
            if ($result !== null || $forceResult) {
                if ($currentKey) {
                    $bucket[$currentKey] = $result;
                    $currentKey = null;
                } else {
                    $bucket[] = $result;
                }

                // If we've just pushed an array, that becomes our new bucket
                if ($result === array()) {
                    // Fetch the key that the array was pushed to
                    end($bucket);
                    $key = key($bucket);
                    reset($bucket);
                    // Store reference to "old" bucket in the stack
                    $bucketStack[$key] = &$bucket;
                    // Set the active bucket to be our newly-pushed, empty array
                    $bucket = &$bucket[$key];
                }
            }
        }

        $result = [$class, $args];
        static::$_cache_parse[$classSpec] = $result;
        return $result;
    }
1146802
public static function loadClass($className)
  {
    if (!class_exists($className)) {
      $path = self::getPath($className);

      $realPath = stream_resolve_include_path($path);

      if (file_exists($realPath)) {
        require_once($realPath);

        return class_exists($className);
      }
      return false;
    } else {
      return true;
    }
  }
587125
public function load($className)
    {
        // get namespace from className
        $namespace = substr($className, 0, strlen($this->namespace.$this->namespaceSeparator));

        // check if requested class must be loaded by this instance of loader
        if ($this->namespace === null || $this->namespace.$this->namespaceSeparator === $namespace) {
            $fileName  = '';
            $namespace = '';

            if (($lastNsPos = strripos($className, $this->namespaceSeparator)) !== false) {
                $namespace = substr($className, 0, $lastNsPos);
                $className = substr($className, $lastNsPos + 1);
                $fileName  = str_replace($this->namespaceSeparator, $this->separator, $namespace).$this->separator;
            }

            $fileName .= str_replace('_', $this->separator, $className).$this->fileExtension;

            if ($this->includePath !== null) {
                $fileName = $this->includePath.$this->separator.$fileName;
            }

            // check first if file exists and load it -> @... suppress is fasted solution ;)
            @include_once $fileName;
        }
    }
1111914
public function doParser(string $className)
    {
        if(!isset($this->data[$className]))
        {
            return;
        }
        // 类
        if(isset($this->data[$className]['class']))
        {
            foreach($this->data[$className]['class'] as $annotation)
            {
                $annotationClassName = get_class($annotation);
                if($this->hasParser($annotationClassName))
                {
                    $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_CLASS, $className);
                }
                else
                {
                    $this->one('parseComplete.' . $annotationClassName, function() use($annotationClassName, $annotation, $className){
                        $annotationClassName = get_class($annotation);
                        if($this->hasParser($annotationClassName))
                        {
                            $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_CLASS, $className);
                        }
                    });
                }
            }
            AnnotationManager::addClassAnnotations($className, ...$this->data[$className]['class']);
        }
        // 属性
        if(isset($this->data[$className]['prop']))
        {
            foreach($this->data[$className]['prop'] as $propName => $annotations)
            {
                foreach($annotations as $annotation)
                {
                    $annotationClassName = get_class($annotation);
                    if($this->hasParser($annotationClassName))
                    {
                        $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_PROPERTY, $propName);
                    }
                    else
                    {
                        $this->one('parseComplete.' . $annotationClassName, function() use($annotationClassName, $annotation, $className, $propName){
                            $annotationClassName = get_class($annotation);
                            if($this->hasParser($annotationClassName))
                            {
                                $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_PROPERTY, $propName);
                            }
                        });
                    }
                }
                AnnotationManager::addPropertyAnnotations($className, $propName, ...$annotations);
            }
        }
        // 方法
        if(isset($this->data[$className]['method']))
        {
            foreach($this->data[$className]['method'] as $methodName => $annotations)
            {
                foreach($annotations as $annotation)
                {
                    $annotationClassName = get_class($annotation);
                    if($this->hasParser($annotationClassName))
                    {
                        $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_METHOD, $methodName);
                    }
                    else
                    {
                        $this->one('parseComplete.' . $annotationClassName, function() use($annotationClassName, $annotation, $className, $methodName){
                            $annotationClassName = get_class($annotation);
                            if($this->hasParser($annotationClassName))
                            {
                                $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_METHOD, $methodName);
                            }
                        });
                    }
                }
                AnnotationManager::addMethodAnnotations($className, $methodName, ...$annotations);
            }
        }
        // 常量
        if(isset($this->data[$className]['const']))
        {
            foreach($this->data[$className]['const'] as $constName => $annotations)
            {
                foreach($annotations as $annotation)
                {
                    $annotationClassName = get_class($annotation);
                    if($this->hasParser($annotationClassName))
                    {
                        $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_CONST, $constName);
                    }
                    else
                    {
                        $this->one('parseComplete.' . $annotationClassName, function() use($annotationClassName, $annotation, $className, $constName){
                            $annotationClassName = get_class($annotation);
                            if($this->hasParser($annotationClassName))
                            {
                                $this->getParser($annotationClassName)->parse($annotation, $className, BaseParser::TARGET_CONST, $constName);
                            }
                        });
                    }
                }
                AnnotationManager::addConstantAnnotations($className, $constName, ...$annotations);
            }
        }
    }
1076873
public function getByClassname(string $classname)
    {
        foreach ($this->container as $value) {
            if (is_object($value) && $value instanceof $classname) {
                return $value;
            }
        }

        return null;
    }
570621
protected function loadClass($className)
    {
        if (strpos($className, NS) !== false) {
            $className = str_replace(NS, DS, ltrim($className, NS));
        }

        $filename = str_replace('_', DS, $className) . '.php';

        if (array_key_exists($className, $this->includePaths)) {
            @require_once $this->includePaths[$className];

            if (class_exists('\\' . trim("$className", '\\'))) {
                return true;
            }
        }

        foreach ($this->includePaths as $namespace => $includePath) {
            if (file_exists($includePath . $filename)) {
                require_once $includePath . $filename;

                return true;
            }
        }

        return false;
    }
831738
private function extractActionFromClass(string $className)
    {
        // If class contain method name
        if (strpos($className, ':') !== false) {
            $classArray = explode(':', $className);
            $classAction = end($classArray);
        } else {
            $classAction = null;
        }
        return $classAction;
    }
1221164
protected function getClassParentModule(
        Container $container,
        $className,
        array $ignoredClasses = [
            ExternalModule::class,
            CompressableExternalModule::class,
            \samson\core\Service::class,
            CompressableService::class
        ]
    ) {
        // Skip ignored class names
        if (!in_array($className, $ignoredClasses, true)) {
            try {
                $instance = $container->get(trim($className));
                $instance->parent = $this->getClassParentModule($container, get_parent_class($instance));
                return $instance;
            } catch (\Exception $exception) {
                return null;
            }
//            // Iterate loaded services
//            foreach ($container->getServices('module') as $service) {
//                if (get_class($service) === $className) {
//                    return $service;
//                }
//            }
        }
        return null;
    }
172424
public function getClassMetadata($className)
    {
        if (!$this->initialized) {
            $this->initialize();
        }
        if (array_key_exists($className, $this->metadata)) {
            return $this->metadata[$className];
        }

        return false;
    }
921085
public function load($className)
    {
        if (isset($this->_classes[$className])) {
            $file = $this->_classes[$className];
            if (!is_file($file)) {
                trigger_error(strtr('load `:class` class failed: `:file` is not exists.', [':class' => $className, ':file' => $file]), E_USER_ERROR);
            }

            //either linux or phar://
            if (PHP_EOL === "\n" || $file[0] === 'p') {
                /** @noinspection PhpIncludeInspection */
                require $file;
            } else {
                $this->requireFile($file);
            }

            return true;
        }

        /** @noinspection LoopWhichDoesNotLoopInspection */
        foreach ($this->_namespaces as $namespace => $path) {
            if (strpos($className, $namespace) !== 0) {
                continue;
            }

            $file = $path . strtr(substr($className, strlen($namespace)), '\\', '/') . '.php';
            if (is_file($file)) {
                //either linux or phar://
                if (PHP_EOL === "\n" || $file[0] === 'p') {
                    /** @noinspection PhpIncludeInspection */
                    require $file;
                } else {
                    $this->requireFile($file);
                }

                return true;
            }
        }

        return false;
    }
888394
public function getSort($className='CSort')
	{
		if(($sort=parent::getSort($className))!==false)
			$sort->modelClass=$this->modelClass;
		return $sort;
	}