| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 | 
							- <?php
 
- /**
 
-  * @link http://www.yiiframework.com/
 
-  * @copyright Copyright (c) 2008 Yii Software LLC
 
-  * @license http://www.yiiframework.com/license/
 
-  */
 
- namespace yii\apidoc\models;
 
- use phpDocumentor\Reflection\FileReflector;
 
- use yii\base\Component;
 
- /**
 
-  *
 
-  * @author Carsten Brandt <mail@cebe.cc>
 
-  * @since 2.0
 
-  */
 
- class Context extends Component
 
- {
 
-     /**
 
-      * @var array list of php files that have been added to this context.
 
-      */
 
-     public $files = [];
 
-     /**
 
-      * @var ClassDoc[]
 
-      */
 
-     public $classes = [];
 
-     /**
 
-      * @var InterfaceDoc[]
 
-      */
 
-     public $interfaces = [];
 
-     /**
 
-      * @var TraitDoc[]
 
-      */
 
-     public $traits = [];
 
-     /**
 
-      * @var array
 
-      */
 
-     public $errors = [];
 
-     /**
 
-      * @var array
 
-      * @since 2.0.6
 
-      */
 
-     public $warnings = [];
 
-     /**
 
-      * Returning TypeDoc for a type given
 
-      * @param string $type
 
-      * @return null|ClassDoc|InterfaceDoc|TraitDoc
 
-      */
 
-     public function getType($type)
 
-     {
 
-         $type = ltrim($type, '\\');
 
-         if (isset($this->classes[$type])) {
 
-             return $this->classes[$type];
 
-         } elseif (isset($this->interfaces[$type])) {
 
-             return $this->interfaces[$type];
 
-         } elseif (isset($this->traits[$type])) {
 
-             return $this->traits[$type];
 
-         }
 
-         return null;
 
-     }
 
-     /**
 
-      * Adds file to context
 
-      * @param string $fileName
 
-      */
 
-     public function addFile($fileName)
 
-     {
 
-         $this->files[$fileName] = sha1_file($fileName);
 
-         $reflection = new FileReflector($fileName, true);
 
-         $reflection->process();
 
-         foreach ($reflection->getClasses() as $class) {
 
-             $class = new ClassDoc($class, $this, ['sourceFile' => $fileName]);
 
-             $this->classes[$class->name] = $class;
 
-         }
 
-         foreach ($reflection->getInterfaces() as $interface) {
 
-             $interface = new InterfaceDoc($interface, $this, ['sourceFile' => $fileName]);
 
-             $this->interfaces[$interface->name] = $interface;
 
-         }
 
-         foreach ($reflection->getTraits() as $trait) {
 
-             $trait = new TraitDoc($trait, $this, ['sourceFile' => $fileName]);
 
-             $this->traits[$trait->name] = $trait;
 
-         }
 
-     }
 
-     /**
 
-      * Updates references
 
-      */
 
-     public function updateReferences()
 
-     {
 
-         // update all subclass references
 
-         foreach ($this->classes as $class) {
 
-             $className = $class->name;
 
-             while (isset($this->classes[$class->parentClass])) {
 
-                 $class = $this->classes[$class->parentClass];
 
-                 $class->subclasses[] = $className;
 
-             }
 
-         }
 
-         // update interfaces of subclasses
 
-         foreach ($this->classes as $class) {
 
-             $this->updateSubclassInterfacesTraits($class);
 
-         }
 
-         // update implementedBy and usedBy for interfaces and traits
 
-         foreach ($this->classes as $class) {
 
-             foreach ($class->traits as $trait) {
 
-                 if (isset($this->traits[$trait])) {
 
-                     $trait = $this->traits[$trait];
 
-                     $trait->usedBy[] = $class->name;
 
-                     $class->properties = array_merge($trait->properties, $class->properties);
 
-                     $class->methods = array_merge($trait->methods, $class->methods);
 
-                 }
 
-             }
 
-             foreach ($class->interfaces as $interface) {
 
-                 if (isset($this->interfaces[$interface])) {
 
-                     $this->interfaces[$interface]->implementedBy[] = $class->name;
 
-                     if ($class->isAbstract) {
 
-                         // add not implemented interface methods
 
-                         foreach ($this->interfaces[$interface]->methods as $method) {
 
-                             if (!isset($class->methods[$method->name])) {
 
-                                 $class->methods[$method->name] = $method;
 
-                             }
 
-                         }
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         foreach ($this->interfaces as $interface) {
 
-             foreach ($interface->parentInterfaces as $pInterface) {
 
-                 if (isset($this->interfaces[$pInterface])) {
 
-                     $this->interfaces[$pInterface]->implementedBy[] = $interface->name;
 
-                 }
 
-             }
 
-         }
 
-         // inherit docs
 
-         foreach ($this->classes as $class) {
 
-             $this->inheritDocs($class);
 
-         }
 
-         // inherit properties, methods, contants and events to subclasses
 
-         foreach ($this->classes as $class) {
 
-             $this->updateSubclassInheritance($class);
 
-         }
 
-         foreach ($this->interfaces as $interface) {
 
-             $this->updateSubInterfaceInheritance($interface);
 
-         }
 
-         // add properties from getters and setters
 
-         foreach ($this->classes as $class) {
 
-             $this->handlePropertyFeature($class);
 
-         }
 
-         // TODO reference exceptions to methods where they are thrown
 
-     }
 
-     /**
 
-      * Add implemented interfaces and used traits to subclasses
 
-      * @param ClassDoc $class
 
-      */
 
-     protected function updateSubclassInterfacesTraits($class)
 
-     {
 
-         foreach ($class->subclasses as $subclass) {
 
-             $subclass = $this->classes[$subclass];
 
-             $subclass->interfaces = array_unique(array_merge($subclass->interfaces, $class->interfaces));
 
-             $subclass->traits = array_unique(array_merge($subclass->traits, $class->traits));
 
-             $this->updateSubclassInterfacesTraits($subclass);
 
-         }
 
-     }
 
-     /**
 
-      * Add implemented interfaces and used traits to subclasses
 
-      * @param ClassDoc $class
 
-      */
 
-     protected function updateSubclassInheritance($class)
 
-     {
 
-         foreach ($class->subclasses as $subclass) {
 
-             $subclass = $this->classes[$subclass];
 
-             $subclass->events = array_merge($class->events, $subclass->events);
 
-             $subclass->constants = array_merge($class->constants, $subclass->constants);
 
-             $subclass->properties = array_merge($class->properties, $subclass->properties);
 
-             $subclass->methods = array_merge($class->methods, $subclass->methods);
 
-             $this->updateSubclassInheritance($subclass);
 
-         }
 
-     }
 
-     /**
 
-      * Add methods to subinterfaces
 
-      * @param InterfaceDoc $class
 
-      */
 
-     protected function updateSubInterfaceInheritance($interface)
 
-     {
 
-         foreach ($interface->implementedBy as $subInterface) {
 
-             if (isset($this->interfaces[$subInterface])) {
 
-                 $subInterface = $this->interfaces[$subInterface];
 
-                 $subInterface->methods = array_merge($interface->methods, $subInterface->methods);
 
-                 $this->updateSubInterfaceInheritance($subInterface);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Inhertit docsblocks using `@inheritDoc` tag.
 
-      * @param ClassDoc $class
 
-      * @see http://phpdoc.org/docs/latest/guides/inheritance.html
 
-      */
 
-     protected function inheritDocs($class)
 
-     {
 
-         // inherit for properties
 
-         foreach ($class->properties as $p) {
 
-             if ($p->hasTag('inheritdoc') && ($inheritTag = $p->getFirstTag('inheritdoc')) !== null) {
 
-                 $inheritedProperty = $this->inheritPropertyRecursive($p, $class);
 
-                 if (!$inheritedProperty) {
 
-                     $this->errors[] = [
 
-                         'line' => $p->startLine,
 
-                         'file' => $class->sourceFile,
 
-                         'message' => "Method {$p->name} has no parent to inherit from in {$class->name}.",
 
-                     ];
 
-                     continue;
 
-                 }
 
-                 // set all properties that are empty.
 
-                 foreach (['shortDescription', 'type', 'types'] as $property) {
 
-                     if (empty($p->$property) || is_string($p->$property) && trim($p->$property) === '') {
 
-                         $p->$property = $inheritedProperty->$property;
 
-                     }
 
-                 }
 
-                 // descriptions will be concatenated.
 
-                 $p->description = trim($p->description) . "\n\n"
 
-                     . trim($inheritedProperty->description) . "\n\n"
 
-                     . $inheritTag->getContent();
 
-                 $p->removeTag('inheritdoc');
 
-             }
 
-         }
 
-         // inherit for methods
 
-         foreach ($class->methods as $m) {
 
-             if ($m->hasTag('inheritdoc') && ($inheritTag = $m->getFirstTag('inheritdoc')) !== null) {
 
-                 $inheritedMethod = $this->inheritMethodRecursive($m, $class);
 
-                 if (!$inheritedMethod) {
 
-                     $this->errors[] = [
 
-                         'line' => $m->startLine,
 
-                         'file' => $class->sourceFile,
 
-                         'message' => "Method {$m->name} has no parent to inherit from in {$class->name}.",
 
-                     ];
 
-                     continue;
 
-                 }
 
-                 // set all properties that are empty.
 
-                 foreach (['shortDescription', 'return', 'returnType', 'returnTypes', 'exceptions'] as $property) {
 
-                     if (empty($m->$property) || is_string($m->$property) && trim($m->$property) === '') {
 
-                         $m->$property = $inheritedMethod->$property;
 
-                     }
 
-                 }
 
-                 // descriptions will be concatenated.
 
-                 $m->description = trim($m->description) . "\n\n"
 
-                     . trim($inheritedMethod->description) . "\n\n"
 
-                     . $inheritTag->getContent();
 
-                 foreach ($m->params as $i => $param) {
 
-                     if (!isset($inheritedMethod->params[$i])) {
 
-                         $this->errors[] = [
 
-                             'line' => $m->startLine,
 
-                             'file' => $class->sourceFile,
 
-                             'message' => "Method param $i does not exist in parent method, @inheritdoc not possible in {$m->name} in {$class->name}.",
 
-                         ];
 
-                         continue;
 
-                     }
 
-                     if (empty($param->description) || trim($param->description) === '') {
 
-                         $param->description = $inheritedMethod->params[$i]->description;
 
-                     }
 
-                     if (empty($param->type) || trim($param->type) === '') {
 
-                         $param->type = $inheritedMethod->params[$i]->type;
 
-                     }
 
-                     if (empty($param->types)) {
 
-                         $param->types = $inheritedMethod->params[$i]->types;
 
-                     }
 
-                 }
 
-                 $m->removeTag('inheritdoc');
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * @param MethodDoc $method
 
-      * @param ClassDoc $class
 
-      * @return mixed
 
-      */
 
-     private function inheritMethodRecursive($method, $class)
 
-     {
 
-         $inheritanceCandidates = array_merge(
 
-             $this->getParents($class),
 
-             $this->getInterfaces($class)
 
-         );
 
-         $methods = [];
 
-         foreach($inheritanceCandidates as $candidate) {
 
-             if (isset($candidate->methods[$method->name])) {
 
-                 $cmethod = $candidate->methods[$method->name];
 
-                 if ($cmethod->hasTag('inheritdoc')) {
 
-                     $this->inheritDocs($candidate);
 
-                 }
 
-                 $methods[] = $cmethod;
 
-             }
 
-         }
 
-         return reset($methods);
 
-     }
 
-     /**
 
-      * @param PropertyDoc $method
 
-      * @param ClassDoc $class
 
-      * @return mixed
 
-      */
 
-     private function inheritPropertyRecursive($method, $class)
 
-     {
 
-         $inheritanceCandidates = array_merge(
 
-             $this->getParents($class),
 
-             $this->getInterfaces($class)
 
-         );
 
-         $properties = [];
 
-         foreach($inheritanceCandidates as $candidate) {
 
-             if (isset($candidate->properties[$method->name])) {
 
-                 $cproperty = $candidate->properties[$method->name];
 
-                 if ($cproperty->hasTag('inheritdoc')) {
 
-                     $this->inheritDocs($candidate);
 
-                 }
 
-                 $properties[] = $cproperty;
 
-             }
 
-         }
 
-         return reset($properties);
 
-     }
 
-     /**
 
-      * @param ClassDoc $class
 
-      * @return array
 
-      */
 
-     private function getParents($class)
 
-     {
 
-         if ($class->parentClass === null || !isset($this->classes[$class->parentClass])) {
 
-             return [];
 
-         }
 
-         return array_merge([$this->classes[$class->parentClass]], $this->getParents($this->classes[$class->parentClass]));
 
-     }
 
-     /**
 
-      * @param ClassDoc $class
 
-      * @return array
 
-      */
 
-     private function getInterfaces($class)
 
-     {
 
-         $interfaces = [];
 
-         foreach($class->interfaces as $interface) {
 
-             if (isset($this->interfaces[$interface])) {
 
-                 $interfaces[] = $this->interfaces[$interface];
 
-             }
 
-         }
 
-         return $interfaces;
 
-     }
 
-     /**
 
-      * Add properties for getters and setters if class is subclass of [[\yii\base\Object]].
 
-      * @param ClassDoc $class
 
-      */
 
-     protected function handlePropertyFeature($class)
 
-     {
 
-         if (!$this->isSubclassOf($class, 'yii\base\Object')) {
 
-             return;
 
-         }
 
-         foreach ($class->getPublicMethods() as $name => $method) {
 
-             if ($method->isStatic) {
 
-                 continue;
 
-             }
 
-             if (!strncmp($name, 'get', 3) && strlen($name) > 3 && $this->hasNonOptionalParams($method)) {
 
-                 $propertyName = '$' . lcfirst(substr($method->name, 3));
 
-                 if (isset($class->properties[$propertyName])) {
 
-                     $property = $class->properties[$propertyName];
 
-                     if ($property->getter === null && $property->setter === null) {
 
-                         $this->errors[] = [
 
-                             'line' => $property->startLine,
 
-                             'file' => $class->sourceFile,
 
-                             'message' => "Property $propertyName conflicts with a defined getter {$method->name} in {$class->name}.",
 
-                         ];
 
-                     }
 
-                     $property->getter = $method;
 
-                 } else {
 
-                     $class->properties[$propertyName] = new PropertyDoc(null, $this, [
 
-                         'name' => $propertyName,
 
-                         'definedBy' => $method->definedBy,
 
-                         'sourceFile' => $class->sourceFile,
 
-                         'visibility' => 'public',
 
-                         'isStatic' => false,
 
-                         'type' => $method->returnType,
 
-                         'types' => $method->returnTypes,
 
-                         'shortDescription' => BaseDoc::extractFirstSentence($method->return),
 
-                         'description' => $method->return,
 
-                         'getter' => $method
 
-                         // TODO set default value
 
-                     ]);
 
-                 }
 
-             }
 
-             if (!strncmp($name, 'set', 3) && strlen($name) > 3 && $this->hasNonOptionalParams($method, 1)) {
 
-                 $propertyName = '$' . lcfirst(substr($method->name, 3));
 
-                 if (isset($class->properties[$propertyName])) {
 
-                     $property = $class->properties[$propertyName];
 
-                     if ($property->getter === null && $property->setter === null) {
 
-                         $this->errors[] = [
 
-                             'line' => $property->startLine,
 
-                             'file' => $class->sourceFile,
 
-                             'message' => "Property $propertyName conflicts with a defined setter {$method->name} in {$class->name}.",
 
-                         ];
 
-                     }
 
-                     $property->setter = $method;
 
-                 } else {
 
-                     $param = $this->getFirstNotOptionalParameter($method);
 
-                     $class->properties[$propertyName] = new PropertyDoc(null, $this, [
 
-                         'name' => $propertyName,
 
-                         'definedBy' => $method->definedBy,
 
-                         'sourceFile' => $class->sourceFile,
 
-                         'visibility' => 'public',
 
-                         'isStatic' => false,
 
-                         'type' => $param->type,
 
-                         'types' => $param->types,
 
-                         'shortDescription' => BaseDoc::extractFirstSentence($param->description),
 
-                         'description' => $param->description,
 
-                         'setter' => $method
 
-                     ]);
 
-                 }
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Check whether a method has `$number` non-optional parameters.
 
-      * @param MethodDoc $method
 
-      * @param int $number number of not optional parameters
 
-      * @return bool
 
-      */
 
-     private function hasNonOptionalParams($method, $number = 0)
 
-     {
 
-         $count = 0;
 
-         foreach ($method->params as $param) {
 
-             if (!$param->isOptional) {
 
-                 $count++;
 
-             }
 
-         }
 
-         return $count == $number;
 
-     }
 
-     /**
 
-      * @param MethodDoc $method
 
-      * @return ParamDoc
 
-      */
 
-     private function getFirstNotOptionalParameter($method)
 
-     {
 
-         foreach ($method->params as $param) {
 
-             if (!$param->isOptional) {
 
-                 return $param;
 
-             }
 
-         }
 
-         return null;
 
-     }
 
-     /**
 
-      * @param ClassDoc $classA
 
-      * @param ClassDoc|string $classB
 
-      * @return bool
 
-      */
 
-     protected function isSubclassOf($classA, $classB)
 
-     {
 
-         if (is_object($classB)) {
 
-             $classB = $classB->name;
 
-         }
 
-         if ($classA->name == $classB) {
 
-             return true;
 
-         }
 
-         while ($classA->parentClass !== null && isset($this->classes[$classA->parentClass])) {
 
-             $classA = $this->classes[$classA->parentClass];
 
-             if ($classA->name == $classB) {
 
-                 return true;
 
-             }
 
-         }
 
-         return false;
 
-     }
 
- }
 
 
  |