/** * {@inheritdoc} */ public function process($elongate = false) { $return = parent::process(true); if (isset($return['type'])) { $return['entity'] = $return['type']; unset($return['type']); } // Property if (preg_match('/(\\w+::)?\\$\\w+/', $return['entity'])) { $return['entity_hint'] = 'property'; } elseif (preg_match('/(\\w+::)?[\\w_]+(\\(\\))/', $return['entity'])) { $return['entity_hint'] = 'method'; } elseif (preg_match('/[\\w_]+/', $return['entity'])) { $return['entity_hint'] = 'class'; } elseif (preg_match('/https?:/', $return['entity'])) { // Used @see when @link was more appropriate $formatter = ConsoleUtil::formatters(); Inconsistency::add('Used @' . $this->tag . ' when @link was more appropriate. => ' . $formatter->gold->apply(SystemStore::get('_.current'))); $return['entity_hint'] = 'uri'; } // Do we need to resolve? if (strpos($return['entity'], '::') !== false) { list($class, $entity) = explode('::', $return['entity']); $class = $this->ancestry->resolveNamespace($class); $return['entity'] = implode('::', array($class, $entity)); } elseif ($return['entity_hint'] === 'method' || $return['entity_hint'] === 'property') { $class = $this->ancestry->getClass(); $return['entity'] = implode('::', array($class, $return['entity'])); } return $return; }
/** * {@inheritdoc} */ public function determine() { // Where are we? SystemStore::add('_.current', preg_replace('/\\[@([^\\]]*)\\]/', ' [@' . $this->tag->getName() . ']', SystemStore::get('_.current'))); $tag = strtolower($this->tag->getName()); $this->triggerEvent("vanity.parse.user.description.{$tag}.pre", new EventStore(array('tag' => &$tag, 'ancestry' => $this->ancestry))); switch ($tag) { case 'example': $processed = new ExampleHandler($this->tag, $this->ancestry); break; case 'inheritdoc': $processed = new InheritdocHandler($this->tag, $this->ancestry); break; case 'internal': $processed = new InternalHandler($this->tag, $this->ancestry); break; case 'link': $processed = new LinkHandler($this->tag, $this->ancestry); break; case 'see': $processed = new SeeHandler($this->tag, $this->ancestry); break; default: $processed = new DefaultHandler($this->tag, $this->ancestry); break; } $this->triggerEvent("vanity.parse.user.description.{$tag}.post", new EventStore(array('tag' => &$tag, 'ancestry' => $this->ancestry))); return $processed; }
/** * {@inheritdoc} */ public function process($elongate = false) { // Used @var, which is deprecated if (strtolower($this->tag) === 'var') { $formatter = ConsoleUtil::formatters(); Inconsistency::add('The @var keyword is deprecated. Use @type instead. => ' . $formatter->gold->apply(SystemStore::get('_.current'))); } return parent::process(true); }
/** * {@inheritdoc} */ public function determine() { // Where are we? SystemStore::add('_.current', preg_replace('/\\[@([^\\]]*)\\]/', ' [@' . $this->tag->getName() . ']', SystemStore::get('_.current'))); $tag = strtolower($this->tag->getName()); $this->triggerEvent("vanity.parse.user.tag.{$tag}.pre", new EventStore(array('tag' => &$tag, 'ancestry' => $this->ancestry))); switch ($tag) { case 'api': $processed = new ApiHandler($this->tag, $this->ancestry); break; case 'author': $processed = new AuthorHandler($this->tag, $this->ancestry); break; case 'copyright': $processed = new CopyrightHandler($this->tag, $this->ancestry); break; case 'deprecated': case 'depreciated': $processed = new DeprecatedHandler($this->tag, $this->ancestry); break; case 'dispatches': case 'event': $processed = new EventHandler($this->tag, $this->ancestry); break; case 'example': $processed = new ExampleHandler($this->tag, $this->ancestry); break; case 'global': $processed = new GlobalHandler($this->tag, $this->ancestry); break; case 'internal': $processed = new InternalHandler($this->tag, $this->ancestry); break; case 'license': $processed = new LicenseHandler($this->tag, $this->ancestry); break; case 'link': $processed = new LinkHandler($this->tag, $this->ancestry); break; case 'method': $processed = new MethodHandler($this->tag, $this->ancestry); break; case 'package': $processed = new PackageHandler($this->tag, $this->ancestry); break; case 'param': $processed = new ParamHandler($this->tag, $this->ancestry); break; case 'property': case 'property-read': case 'property-write': $processed = new PropertyHandler($this->tag, $this->ancestry); break; case 'return': case 'returns': $processed = new ReturnHandler($this->tag, $this->ancestry); break; case 'alias': case 'see': $processed = new SeeHandler($this->tag, $this->ancestry); break; case 'since': case 'available': $processed = new SinceHandler($this->tag, $this->ancestry); break; case 'throw': case 'throws': $processed = new ThrowHandler($this->tag, $this->ancestry); break; case 'todo': case 'fixme': $processed = new TodoHandler($this->tag, $this->ancestry); break; case 'type': case 'var': $processed = new TypeHandler($this->tag, $this->ancestry); break; case 'uses': case 'used-by': $processed = new UsesHandler($this->tag, $this->ancestry); break; case 'version': $processed = new VersionHandler($this->tag, $this->ancestry); break; default: $processed = new DefaultHandler($this->tag, $this->ancestry); break; } $this->triggerEvent("vanity.parse.user.tag.{$tag}.post", new EventStore(array('tag' => &$tag, 'ancestry' => $this->ancestry))); return $processed; }
public static function resolve($reflected) { $is_method = false; $is_property = false; // Are we working with a property or a method? if ($reflected instanceof ReflectionMethod) { $is_method = true; } elseif ($reflected instanceof ReflectionProperty) { $is_property = true; } else { throw new Exception('Only methods and properties can be reflected with ' . get_called_class()); } // Parse the docblock $docblock = new DocBlock($reflected->getDocComment()); $found_description = false; $return = $reflected; // Save these for messaging $__class = $reflected->getDeclaringClass()->getName(); $__kind = $reflected->getName(); // Can we just do a straight-up inherit? // @todo: Do a better job of handling {@inheritdoc} according to the spec. try { while (!$found_description && strpos($docblock->getShortDescription(), '{@inheritdoc}') !== false) { // Start over... $found_description = false; // Log that we're starting... Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} Starting resolution:', array(sprintf("{$__class}%s{$__kind}%s", $is_method ? '::' : '::$', $is_method ? '()' : ''))); // Grab a reference to the class containing the entity with the {@inheritdoc} tag $klass = $reflected->getDeclaringClass(); // Is this an Interface? if ($klass->isInterface()) { throw new InheritdocInInterfaceException('The {@inheritdoc} tag is not resolvable from within Interfaces. Methods and properties should ' . 'be fully-documented.'); } elseif (SystemStore::get('_.php54') && $klass->isTrait()) { throw new InheritdocInTraitException('The {@inheritdoc} tag is not resolvable from within Traits. Methods and properties should ' . 'be fully-documented.'); } // Are we using Interfaces? if (!$found_description && ($interface_count = count($klass->getInterfaces())) > 0) { $count = 1; foreach ($klass->getInterfaces() as $rinterface) { Logger::get()->{ConfigStore::get('log.info')}("{@inheritdoc} Checking Interface {$count}/{$interface_count}:", array($rinterface->getName())); try { $return = $rinterface->getMethod($reflected->getName()); Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} Match!', array($rinterface->getName(), $reflected->getName(), 'Method')); $found_description = true; break 2; } catch (Exception $e) { try { $return = $rinterface->getProperty($reflected->getName()); Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} Match!', array($rinterface->getName(), $reflected->getName(), 'Property')); $found_description = true; break 2; } catch (Exception $e) { Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} No match. Will keep looking...', array($rinterface->getName(), $reflected->getName())); } } $count++; } } // Are we using Traits? if (!$found_description && SystemStore::get('_.php54') && ($trait_count = count($klass->getTraits())) > 0) { $count = 1; foreach ($klass->getTraits() as $rtrait) { Logger::get()->{ConfigStore::get('log.info')}("{@inheritdoc} Checking Trait {$count}/{$trait_count}:", array($rtrait->getName())); try { $return = $rtrait->getMethod($reflected->getName()); Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} Match!', array($rtrait->getName(), $reflected->getName(), 'Method')); $found_description = true; break 2; } catch (Exception $e) { try { $return = $rtrait->getProperty($reflected->getName()); Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} Match!', array($rtrait->getName(), $reflected->getName(), 'Property')); $found_description = true; break 2; } catch (Exception $e) { Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} No match. Will keep looking...', array($rtrait->getName(), $reflected->getName())); } } $count++; } } // Are we extending a class? if ($klass->getParentClass()) { // Continue climbing up the ancestry as necessary while (!$found_description && $klass->getParentClass()) { // Rewrite the reference to $klass $klass = $klass->getParentClass(); Logger::get()->{ConfigStore::get('log.info')}("{@inheritdoc} Checking the parent class:", array($klass->getName())); try { $return = $klass->getMethod($reflected->getName()); Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} Match!', array($klass->getName(), $reflected->getName(), 'Method')); $found_description = true; break 2; } catch (Exception $e) { try { $return = $klass->getProperty($reflected->getName()); Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} Match!', array($klass->getName(), $reflected->getName(), 'Property')); $found_description = true; break 2; } catch (Exception $e) { Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} No match. Will keep looking...', array($klass->getName(), $reflected->getName())); } } } } // We couldn't find anything throw new CouldNotResolveInheritdocException('Leaving as-is. The tag will be viewable in the ' . 'resulting documentation.'); } } catch (InheritdocInInterfaceException $e) { $message = sprintf("{$__class}%s{$__kind}%s", $is_method ? '::' : '::$', $is_method ? '()' : ''); // Log that we're starting... Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} ' . $e->getMessage(), array($message)); $formatter = ConsoleUtil::formatters(); Inconsistency::add($message . $formatter->gold->apply(' => Could not resolve {@inheritdoc}. ' . $e->getMessage())); } catch (InheritdocInTraitException $e) { $message = sprintf("{$__class}%s{$__kind}%s", $is_method ? '::' : '::$', $is_method ? '()' : ''); // Log that we're starting... Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} ' . $e->getMessage(), array($message)); $formatter = ConsoleUtil::formatters(); Inconsistency::add($message . $formatter->gold->apply(' => Could not resolve {@inheritdoc}. ' . $e->getMessage())); } catch (CouldNotResolveInheritdocException $e) { $message = sprintf("{$__class}%s{$__kind}%s", $is_method ? '::' : '::$', $is_method ? '()' : ''); // Log that we're starting... Logger::get()->{ConfigStore::get('log.info')}('{@inheritdoc} ' . $e->getMessage(), array($message)); $formatter = ConsoleUtil::formatters(); Inconsistency::add($message . $formatter->gold->apply(' => Could not resolve {@inheritdoc}. ' . $e->getMessage())); } catch (VanityException $e) { } catch (Exception $e) { } return $return; }
/** * Copyright (c) 2009-2012 [Ryan Parman](http://ryanparman.com) * Copyright (c) 2011-2012 [Amazon Web Services, Inc.](http://aws.amazon.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * <http://www.opensource.org/licenses/mit-license.php> */ /********************************************************/ // AVAILABLE CONFIGURATION-RELATED OPTIONS use Symfony\Component\Console\Input\InputOption; use Vanity\System\Store as SystemStore; return array('vanity' => array('view_config' => array(InputOption::VALUE_NONE, 'Display Vanity\'s configuration settings, and stop.', null), 'config_dir' => array(InputOption::VALUE_OPTIONAL, 'The directory which contains the project\'s Vanity configuration. Vanity will use this for resolving default paths.', SystemStore::get('_.project_config_dir')), 'bootstrap' => array(InputOption::VALUE_OPTIONAL, 'This file is loaded first. Useful for telling Vanity how to load project classes, settings constants, or other things.', '%VANITY.CONFIG_DIR%/bootstrap.php'), 'name' => array(InputOption::VALUE_OPTIONAL, 'The name of the product.', pathinfo(VANITY_PROJECT_WORKING_DIR, PATHINFO_FILENAME)), 'stage' => array(InputOption::VALUE_OPTIONAL, 'The stage that the project is currently in. Can be any ASCII value. (e.g., development, alpha, beta, rc, production).', 'development'), 'version' => array(InputOption::VALUE_OPTIONAL, 'The version number we should use.', 'latest'), 'reports' => array(InputOption::VALUE_OPTIONAL, 'The location to use for writing various reports that have been enabled.', '%VANITY.CONFIG_DIR%/reports')), 'log' => array('aliases' => array(InputOption::VALUE_OPTIONAL, 'Which severity should Alias/Ancestry Resolution messages have?', 'debug'), 'commands' => array(InputOption::VALUE_OPTIONAL, 'Which severity should Command begin/end messages have?', 'debug'), 'error' => array(InputOption::VALUE_OPTIONAL, 'Which severity should Error messages have?', 'error'), 'events' => array(InputOption::VALUE_OPTIONAL, 'Which severity should Event Trigger messages have?', 'debug'), 'info' => array(InputOption::VALUE_OPTIONAL, 'Which severity should Notice messages have?', 'info'), 'warn' => array(InputOption::VALUE_OPTIONAL, 'Which severity should Warning messages have?', 'warn')), 'report' => array('dependencies' => array(InputOption::VALUE_OPTIONAL, 'Write a report if there are environment dependencies for the project.', false), 'inconsistencies' => array(InputOption::VALUE_OPTIONAL, 'Write a report if there are documentation inconsistencies.', false), 'todo' => array(InputOption::VALUE_OPTIONAL, 'Write a report if there are TODOs.', false), 'ungrouped' => array(InputOption::VALUE_OPTIONAL, 'Write a report if there are ungrouped methods.', false)), 'warn' => array('dependencies' => array(InputOption::VALUE_OPTIONAL, 'Warn on the console if there are environment dependencies for the project.', false), 'inconsistencies' => array(InputOption::VALUE_OPTIONAL, 'Warn on the console if there are documentation inconsistencies.', false), 'todo' => array(InputOption::VALUE_OPTIONAL, 'Warn on the console if there are TODOs.', false), 'ungrouped' => array(InputOption::VALUE_OPTIONAL, 'Warn on the console if there are ungrouped methods.', false)));
/** * Resolves a namespace alias into a fully-qualified namespace. * * @param string $short A shortened namespace alias. * @return string The fully-qualified namespace, if available. */ public function resolveNamespace($short) { if (isset($this->aliases[$short])) { Logger::get()->{ConfigStore::get('log.aliases')}('Aliases: Matched in the list of known aliases.', array($short, $this->aliases[$short])); return $this->aliases[$short]; } else { // Handle implicit aliases in the same namespace. try { $namespace = $this->class->getNamespaceName() . '\\' . $short; new ReflectionClass($namespace); Logger::get()->{ConfigStore::get('log.aliases')}('Aliases: Matched in the current namespace.', array($short, $namespace)); // If we didn't throw an exception, we're good. return $namespace; } catch (ReflectionException $e) { // Handle implicit namespaces in an extended/implemented namespace. try { foreach ($this->namespaces as $ns) { try { $namespace = $ns . '\\' . $short; new ReflectionClass($namespace); Logger::get()->{ConfigStore::get('log.aliases')}('Aliases: Matched in an extended/implemented namespace.', array($short, $namespace)); // If we didn't throw an exception, we're good. return $namespace; } catch (ReflectionException $e) { } } throw new ReflectionException(); } catch (ReflectionException $e) { // Try removing the beginning '\' to see if we find a match. try { $class = preg_replace('/^\\\\/', '', $short); new ReflectionClass($class); Logger::get()->{ConfigStore::get('log.aliases')}('Aliases: Matched by stripping the \\ prefix.', array($short, $class)); // If we didn't throw an exception, we're good. return $class; } catch (ReflectionException $e) { $formatter = ConsoleUtil::formatters(); Inconsistency::add($class . $formatter->gold->apply(' => No match found for ' . $short . ' (' . SystemStore::get('_.current') . ')')); Logger::get()->{ConfigStore::get('log.aliases')}('Aliases: No match found.', array($short)); // No match. Return it as-is (without any starting backslash). return $class; } } } } }
/** * Retrieve the properties for the class. * * @return array A list of properties. */ public function getProperties() { $rproperties = $this->class->getProperties(); foreach ($rproperties as $rproperty) { if (!isset($this->properties['count'])) { $this->properties['count'] = count($rproperties); } if (!isset($this->properties['property'])) { $this->properties['property'] = array(); } $rproperty = InheritdocHandler::resolve($rproperty); $_tags = new TagHandler($rproperty->getDocComment(), $this->ancestry); $property_docblock = new DocBlock($rproperty->getDocComment()); // Property-specific data $entry = array(); $entry['name'] = $rproperty->getName(); $entry['visibility'] = $this->access($rproperty); // Where are we? SystemStore::add('_.current', $this->class->getName() . '::$' . $rproperty->getName()); if ($description = $_tags->getDescription()) { $entry['description'] = $description; } // Property inheritance if (($declaring_class = $rproperty->getDeclaringClass()->getName()) !== $this->class->getName()) { if (!isset($entry['inheritance'])) { $entry['inheritance'] = array(); } if (!isset($entry['inheritance']['class'])) { $entry['inheritance']['class'] = array(); } $declaring_class = $rproperty->getDeclaringClass(); $subentry = array(); $subentry['name'] = $declaring_class->getName(); if ($declaring_class->getFileName()) { $subentry['path'] = str_replace(VANITY_PROJECT_WORKING_DIR . '/', '', $declaring_class->getFileName()); } $entry['inheritance']['class'][] = $subentry; } // Default value, if accessible if ($rproperty->isPublic()) { $rvalue = $rproperty->getValue($this->class); $adjusted_rvalue = null; switch (strtolower(gettype($rvalue))) { case 'boolean': $adjusted_rvalue = $rvalue == 1 ? 'true' : 'false'; break; case 'null': $adjusted_rvalue = 'null'; break; case 'string': $adjusted_rvalue = $rvalue; break; case 'integer': $adjusted_rvalue = (int) $rvalue; break; case 'array': $adjusted_rvalue = ParseUtil::unwrapArray($rvalue); break; } $entry['initializer'] = array(); $entry['initializer']['type'] = gettype($rvalue); $entry['initializer']['value'] = $adjusted_rvalue; } // Property tags if ($t = $_tags->getTags()) { $entry['metadata'] = $t; } $this->properties['property'][] = $entry; } return $this->properties; }
/** * Retrieve the properties for the class. * * @return array A list of properties. */ public function getMethods() { $rclass_methods = $this->class->getMethods(); // Add methods and parameters $rclass_methods = array_values(array_filter($rclass_methods, function ($rmethod) { if ($exclusions = ConfigStore::get('source.exclude.methods')) { return !preg_match($exclusions, $rmethod->getName()); } return true; })); foreach ($rclass_methods as $rmethod) { $documentThis = true; if (!isset($this->methods['count'])) { $this->methods['count'] = count($rclass_methods); } if (!isset($this->methods['method'])) { $this->methods['method'] = array(); } $rmethod = InheritdocHandler::resolve($rmethod); $_tags = new TagHandler($rmethod->getDocComment(), $this->ancestry); $method_docblock = new DocBlock($rmethod->getDocComment()); $entry = array(); $entry['name'] = $rmethod->getName(); $entry['visibility'] = $this->methodAccess($rmethod); if ($extension = $rmethod->getExtensionName()) { $entry['extension'] = $extension; DependencyCollector::add($extension); } if ($rmethod->getFileName()) { $entry['path'] = str_replace(array(VANITY_SYSTEM . '/', VANITY_PROJECT_WORKING_DIR . '/'), '', $rmethod->getFileName()); $entry['lines'] = array('start' => $rmethod->getStartLine(), 'end' => $rmethod->getEndLine()); if ($viewsource = ConfigStore::get('source.viewsource')) { $entry['viewsource'] = str_replace(array('%PATH%', '%LINE%'), array($entry['path'], $entry['lines']['start']), $viewsource); } } if ($description = $_tags->getDescription()) { $entry['description'] = $description; } // Method inheritance if (($declaring_class = $rmethod->getDeclaringClass()->getName()) !== $this->class->getName()) { if (!isset($entry['inheritance'])) { $entry['inheritance'] = array(); } if (!isset($entry['inheritance']['class'])) { $entry['inheritance']['class'] = array(); } $declaring_class = new ReflectionClass($declaring_class); $subentry = array(); $subentry['name'] = $declaring_class->getName(); if ($declaring_class->getFileName()) { $subentry['path'] = str_replace(VANITY_PROJECT_WORKING_DIR . '/', '', $declaring_class->getFileName()); } $entry['inheritance']['class'][] = $subentry; } // Method tags if (count($method_docblock->getTags())) { if (!isset($entry['metadata'])) { $entry['metadata'] = array(); } if (!isset($entry['metadata']['tag'])) { $entry['metadata']['tag'] = array(); } foreach ($method_docblock->getTags() as $rtag) { $dtag = new Tag($rtag, $this->ancestry); $tagData = $dtag->determine()->process(ConfigStore::get('source.resolve_aliases')); if ($tagData['name'] === 'alias') { SystemStore::add('alias.' . $tagData['entity'], $this->class->getName() . '::' . $rmethod->getName()); $documentThis = false; } $entry['metadata']['tag'][] = $tagData; } } // Method parameters if ($count = count($rmethod->getParameters())) { if (!isset($entry['parameters'])) { $entry['parameters'] = array(); } if (!isset($entry['parameters']['count'])) { $entry['parameters']['count'] = $count; } if (!isset($entry['parameters']['parameter'])) { $entry['parameters']['parameter'] = array(); } foreach ($rmethod->getParameters() as $rparameter) { $tag_finder = new TagFinder($entry); $param = array(); $param['name'] = $rparameter->getName(); $param['required'] = !$rparameter->isOptional(); $param['passed_by_reference'] = $rparameter->isPassedByReference(); if ($rparameter->isDefaultValueAvailable()) { $param['default'] = $rparameter->getDefaultValue(); } // Pull-in from @tags if ($_description = $tag_finder->find('description', $param['name'])) { $param['description'] = $_description; } if ($_type = $tag_finder->find('type', $param['name'])) { $param['type'] = $this->ancestry->resolveNamespace($_type); } if ($_types = $tag_finder->find('types', $param['name'])) { $param['types'] = $_types; } // Clean-up parameter metadata tags if (isset($entry['metadata']) && isset($entry['metadata']['tag'])) { foreach ($entry['metadata']['tag'] as $index => $tag) { if ($tag['name'] === 'param' && $tag['variable'] === $param['name']) { unset($entry['metadata']['tag'][$index]); } } } // Type hinting trumps docblock if ($rparameter->getClass()) { if (isset($param['type']) && $param['type'] !== $rparameter->getClass()->getName()) { // @todo: Resolve namespace of declaring class. Inconsistency::add($this->class->getName() . '::' . $rmethod->getName() . '($' . $rparameter->getName() . ') [' . $param['type'] . ' => ' . $rparameter->getClass()->getName() . ']'); } $param['type'] = $rparameter->getClass()->getName(); if (isset($param['types'])) { unset($param['types']); } } $entry['parameters']['parameter'][] = $param; } } // Return value $entry['return'] = array('type' => 'void'); if (isset($entry['metadata']) && isset($entry['metadata']['tag'])) { foreach ($entry['metadata']['tag'] as $index => $tag) { if ($tag['name'] === 'return') { $entry['return'] = $tag; unset($entry['return']['name']); // Clean-up return metadata tags unset($entry['metadata']['tag'][$index]); } } } if ($documentThis) { $this->methods['method'][] = $entry; } } return $this->methods; }
/** * An array of file paths retrieved from {@see files()}. * * @param array $files A list of file paths. * @return array A list of classes. */ public static function classes(array $files) { $loader = new UniversalClassLoader(); // Support PSR-0 autoloading with a composer.json file // @todo: Add support for Composer's classmap autoloading. if (file_exists(VANITY_PROJECT_WORKING_DIR . '/vendor/composer/autoload_namespaces.php')) { // Register namespaces with the class loader $loader->registerNamespaces(include VANITY_PROJECT_WORKING_DIR . '/vendor/composer/autoload_namespaces.php'); } elseif (file_exists(VANITY_PROJECT_WORKING_DIR . '/composer.json')) { // Register namespaces with the class loader $composer = json_decode(file_get_contents(VANITY_PROJECT_WORKING_DIR . '/composer.json'), true); if (isset($composer['autoload']) && isset($composer['autoload']['psr-0'])) { $loader->registerNamespaces($composer['autoload']['psr-0']); } } $loader->register(); $class_list = array(); // Collect all current classes, interfaces and traits if (SystemStore::get('_.php54')) { $before = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); } else { $before = array_merge(get_declared_classes(), get_declared_interfaces()); } foreach ($files as $file) { include_once $file; } // Handle traits if this version of PHP supports them. if (SystemStore::get('_.php54')) { $after = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); } else { $after = array_merge(get_declared_classes(), get_declared_interfaces()); } // We should be able to document ourselves if (defined('VANITY_AM_I')) { $after = array_filter($after, function ($class) { return substr($class, 0, 7) !== 'Vanity\\'; }); } $class_list = array_values(array_unique(array_diff($after, $before))); sort($class_list); return $class_list; }
/** * Retrieve the constants for the class. * * @return array A list of constants. */ public function getConstants() { $rclass_constants = $this->class->getConstants(); ksort($rclass_constants); // Add constants foreach ($rclass_constants as $rconstant_name => $rconstant_value) { if (!isset($this->constants['count'])) { $this->constants['count'] = count($rclass_constants); } if (!isset($this->constants['constant'])) { $this->constants['constant'] = array(); } $entry = array(); $entry['name'] = $rconstant_name; $entry['value'] = $rconstant_value; $entry['type'] = gettype($rconstant_value); // Where are we? SystemStore::add('_.current', $this->class->getName() . '::' . $rconstant_name); $this->constants['constant'][] = $entry; } return $this->constants; }
/** * Trigger an event containing the log path. * * @return void */ public function triggerLogMessageEvent() { $this->triggerEvent('vanity.command.log_path', new EventStore(array('log_path' => SystemStore::get('_.log_dir'), 'time' => SystemStore::get('_.run_time')))); }
/** * Reflects over the given class and produces an associative array * containing all relevant class data. * * @event EventStore vanity.parse.user.reflect.pre * @event EventStore vanity.parse.user.reflect.post * @return array An associative array containing all relevant class data. */ public function process() { // REFLECT ALL THE THINGS! $rclass_methods = $this->rclass->getMethods(); $long_filename = $this->rclass->getFileName(); $short_filename = str_replace(array(VANITY_SYSTEM . '/', VANITY_PROJECT_WORKING_DIR . '/'), '', $long_filename); $this->triggerEvent("vanity.parse.user.reflect.pre", new EventStore(array('data' => &$this->data))); $this->data['name'] = $this->rclass->getShortName(); $this->data['namespace'] = $this->rclass->getNamespaceName(); $this->data['full_name'] = $this->class_name; $this->data['namespace_as_path'] = str_replace('\\', '/', $this->class_name); $this->data['path'] = $short_filename; if (SystemStore::get('_.php54') && $this->rclass->isTrait()) { $this->data['kind'] = 'Trait'; } elseif ($this->rclass->isInterface()) { $this->data['kind'] = 'Interface'; } elseif ($this->rclass->isSubClassOf('Exception')) { $this->data['kind'] = 'Exception'; } else { $this->data['kind'] = 'Class'; } SystemStore::add('_.current', $this->class_name); #--------------------------------------------------------------------------# // Enable GitHub lookups for author data if (ConfigStore::get('source.github.user') && ConfigStore::get('source.github.pass')) { $github = new GitHub(ConfigStore::get('source.github.user'), ConfigStore::get('source.github.pass')); $github->setRepository(ConfigStore::get('source.github.repo_owner'), ConfigStore::get('source.github.repo_name')); $this->data['github'] = $github->getAuthorsForFile($this->data['path']); } #--------------------------------------------------------------------------# // Add description if ($description = $this->class_tags->getDescription()) { $this->data['description'] = $description; } // Add inheritance chain if ($this->inheritance) { $this->data['inheritance'] = $this->inheritance; } // Add implemented interfaces if ($this->implements) { $this->data['implements'] = $this->implements; } // Add used traits if ($this->traits) { $this->data['traits'] = $this->traits; } // Add class tags if ($tags = $this->class_tags->getTags()) { $this->data['metadata'] = $tags; } // Add constants if ($constants = $this->constants->getConstants()) { $this->data['constants'] = $constants; } // Add properties if ($properties = $this->properties->getProperties()) { $this->data['properties'] = $properties; } // Add meta-properties if (isset($this->data['metadata']) && isset($this->data['properties'])) { $new = $this->formatMetaProperties($this->data['metadata']); $this->data['properties']['property'] = array_merge($this->data['properties']['property'], $new); $this->data['properties']['count'] += count($new); } elseif (isset($this->data['metadata'])) { $new = $this->formatMetaProperties($this->data['metadata']); $this->data['properties']['property'] = $new; $this->data['properties']['count'] = count($new); } // Sort the properties alphabetically if (isset($this->data['properties']) && isset($this->data['properties']['property']) && is_array($this->data['properties']['property'])) { usort($this->data['properties']['property'], function ($a, $b) { $a = $a['name']; $b = $b['name']; if ($a === $b) { return 0; } return $a < $b ? -1 : 1; }); } // Add methods if ($methods = $this->methods->getMethods()) { $this->data['methods'] = $methods; } // Add meta-methods if (isset($this->data['metadata']) && isset($this->data['methods'])) { $new = $this->formatMetaMethods($this->data['metadata']); $this->data['methods']['method'] = array_merge($this->data['methods']['method'], $new); $this->data['methods']['count'] += count($new); } elseif (isset($this->data['metadata'])) { $new = $this->formatMetaMethods($this->data['metadata']); $this->data['methods']['method'] = $new; $this->data['methods']['count'] = count($new); } // Sort the methods alphabetically if (isset($this->data['methods']) && isset($this->data['methods']['method']) && is_array($this->data['methods']['method'])) { usort($this->data['methods']['method'], function ($a, $b) { $a = $a['name']; $b = $b['name']; if ($a === $b) { return 0; } return $a < $b ? -1 : 1; }); } // Sort the metadata tags, post-edit from @method, @property and @return if (isset($this->data['metadata'])) { $this->data['metadata']['tag'] = array_values($this->data['metadata']['tag']); // Sort the tags alphabetically usort($this->data['metadata']['tag'], function ($a, $b) { $a = $a['name']; $b = $b['name']; if ($a === $b) { return 0; } return $a < $b ? -1 : 1; }); } // Handle alias tags $this->triggerEvent("vanity.parse.user.reflect.post", new EventStore(array('data' => &$this->data))); }
/** * Return the config values passed in the config.yml file. * * @return array The config values passed in the config.yml file. */ private function fileValues() { // Use the config directory passed to the CLI $config_dir = $this->cliValues(true) ?: SystemStore::get('_.project_config_dir'); if (file_exists($config_dir . '/config.yml')) { ConfigStore::$messages[] = 'Merged configuration options from ' . $this->formatter->info->apply(' ' . $config_dir . '/config.yml '); $options = YAML::parse($config_dir . '/config.yml'); $config = ConfigStore::convert($options); $config = array_filter($config); return $config; } return array(); }