Returns various information on the methods of an object, in different formats.
public static methods ( mixed $class, string $format = null, array $options = [] ) : mixed | ||
$class | mixed | A string class name or an object instance, from which to get methods. |
$format | string | The type and format of data to return. Available options are: - `null`: Returns a `Collection` object containing a `ReflectionMethod` instance for each method. - `'extents'`: Returns a two-dimensional array with method names as keys, and an array with starting and ending line numbers as values. - `'ranges'`: Returns a two-dimensional array where each key is a method name, and each value is an array of line numbers which are contained in the method. |
$options | array | Set of options applied directly (check `_items()` for more options): - `'methods'` _array_: An arbitrary list of methods to search, as a string (single method name) or array of method names. - `'group'`: If true (default) the array is grouped by context (ex.: method name), if false the results are sequentially appended to an array. -'self': If true (default), only returns properties defined in `$class`, excluding properties from inherited classes. |
return | mixed | Return value depends on the $format given: - `null` on failure. - `lithium\util\Collection` if $format is `null` - `array` if $format is either `'extends'` or `'ranges'`. |
/** * "Explain" methods step. * * @return void */ public function explainExplainMethods() { $this->header("The Explain Methods"); $this->out("The next step is to create your first `explain` method. When an exercise is run, each user-defined method that begins with 'explain' is run in the order it was defined.\n"); $this->out("Let's start by defining a new method called `explainIntro`."); $this->halt(); $methods = \lithium\analysis\Inspector::methods('\\li3_exercises\\extensions\\exercises\\Blog', 'extents'); $this->assertTrue(isset($methods['explainIntro']), "Hmmm. I can't seem to find a method defined on your new Blog class named '{:purple}explainIntro{:end}'. {:error}Make sure it has public visibility!{:end}"); }
/** * Creates step list for this exercise. * * @return void */ protected function _initSteps() { $methods = Inspector::methods($this)->to('array'); foreach ($methods as $method) { if (substr($method['name'], 0, strlen($this->_methodPrefix)) === $this->_methodPrefix) { $this->_steps[] = $method['name']; } } }
protected static function _extractClassCode($ref) { $method = $ref['method']; $class = $ref['class']; $markers = Inspector::methods($class, 'extents', array('methods' => array($ref['method']))); $methodStart = $markers[$method][0]; list($start, $end) = array_map('intval', explode('-', $ref['lines'])); $code = Inspector::lines($class, range($start + $methodStart, $end + $methodStart)); $pad = substr_count(current($code), "\t", 0); $lines = array_map('substr', $code, array_fill(0, count($code), $pad)); return array("{$class}::{$method}({$start}-{$end})", join("\n", $lines)); }
/** * Get the methods to test. * * @param string $request * @return string */ protected function _methods($request) { $use = $this->_use($request); $path = Libraries::path($use); if (!file_exists($path)) { return ""; } $methods = (array) Inspector::methods($use, 'extents'); $testMethods = array(); foreach (array_keys($methods) as $method) { $testMethods[] = "\tpublic function test" . ucwords($method) . "() {}"; } return join("\n", $testMethods); }
/** * Takes an instance of an object (usually a Collection object) containing test * instances. Introspects the test subject classes to extract cyclomatic complexity data. * * @param object $report Instance of Report which is calling apply. * @param array $tests The test to apply this filter on * @param array $options Not used. * @return object|void Returns the instance of `$tests`. */ public static function apply($report, $tests, array $options = array()) { $results = array(); foreach ($tests->invoke('subject') as $class) { $results[$class] = array(); if (!($methods = Inspector::methods($class, 'ranges', array('public' => false)))) { continue; } foreach ($methods as $method => $lines) { $lines = Inspector::lines($class, $lines); $branches = Parser::tokenize(join("\n", (array) $lines), array('include' => static::$_include)); $results[$class][$method] = count($branches) + 1; $report->collect(__CLASS__, $results); } } return $tests; }
/** * Generate test cases in the given namespace. * `li3 create test model Post` * `li3 create test --library=li3_plugin model Post` * * @param string $type namespace of the class (e.g. model, controller, some.name.space). * @param string $name Name of class to test. * @return void */ public function run($type = null, $name = null) { $library = Libraries::get($this->library); if (empty($library['prefix'])) { return false; } $namespace = $this->_namespace($type); $use = "\\{$library['prefix']}{$namespace}\\{$name}"; $methods = array(); if (class_exists($use)) { $methods = array(); foreach (array_keys(Inspector::methods($use, 'extents')) as $method) { $methods[] = "\tpublic function test" . ucwords($method) . "() {}"; } } $params = array('namespace' => "{$library['prefix']}tests\\cases\\{$namespace}", 'use' => $use, 'class' => "{$name}Test", 'methods' => join("\n", $methods)); if ($this->_save($this->template, $params)) { $this->out("{$params['class']} created for {$name} in {$params['namespace']}."); return true; } return false; }
/** * Helper method for caching closure function references to help the process of building the * stack trace. * * @param array $frame Backtrace information. * @param callable|string $function The method related to $frame information. * @return string Returns either the cached or the fetched closure function reference while * writing its reference to the cache array `$_closureCache`. */ protected static function _closureDef($frame, $function) { $reference = '::'; $frame += array('file' => '??', 'line' => '??'); $cacheKey = "{$frame['file']}@{$frame['line']}"; if (isset(static::$_closureCache[$cacheKey])) { return static::$_closureCache[$cacheKey]; } if ($class = Inspector::classes(array('file' => $frame['file']))) { foreach (Inspector::methods(key($class), 'extents') as $method => $extents) { $line = $frame['line']; if (!($extents[0] <= $line && $line <= $extents[1])) { continue; } $class = key($class); $reference = "{$class}::{$method}"; $function = "{$reference}()::{closure}"; break; } } else { $reference = $frame['file']; $function = "{$reference}::{closure}"; } $line = static::_definition($reference, $frame['line']) ?: '?'; $function .= " @ {$line}"; return static::$_closureCache[$cacheKey] = $function; }
protected static function _class(array $object, array $data, array $options = array()) { $identifier = $object['identifier']; $proto = array('parent' => get_parent_class($identifier), 'methods' => Inspector::methods($identifier, null, array('public' => false)), 'properties' => Inspector::properties($identifier, array('public' => false))); $classes = Libraries::find($object['library'], array('recursive' => true)); $proto['subClasses'] = array_filter($classes, function ($class) use($identifier) { if (preg_match('/\\\\(libraries)\\\\|\\\\(mocks)\\\\|\\\\(tests)\\\\/', $class)) { return false; } try { return get_parent_class($class) == $identifier; } catch (Exception $e) { return false; } }); sort($proto['subClasses']); return $proto + $object + array('tags' => isset($data['tags']) ? $data['tags'] : array()); }
/** * Get the methods for the class * * @param string $class * @param array $options * @return array */ protected function _methods($class, $options = array()) { $defaults = array('name' => null); $options += $defaults; $methods = Inspector::methods($class)->map(function ($item) { if ($item->name[0] === '_') { return; } $modifiers = array_values(Inspector::invokeMethod('_modifiers', array($item))); $setAccess = array_intersect($modifiers, array('private', 'protected')) != array(); if ($setAccess) { $item->setAccessible(true); } $result = compact('modifiers') + array('docComment' => $item->getDocComment(), 'name' => $item->getName()); if ($setAccess) { $item->setAccessible(false); } return $result; }, array('collect' => false)); $results = array(); foreach ($methods as $method) { $comment = Docblock::comment($method['docComment']); $name = $method['name']; $description = $comment['description']; $args = !isset($comment['tags']['params']) ? null : $comment['tags']['params']; $return = !isset($comment['tags']['return']) ? null : trim(strtok($comment['tags']['return'], ' ')); $command = $name === 'run' ? null : $name; $command = !$command && !empty($args) ? '[ARGS]' : $command; $usage = "{$command} "; $usage .= empty($args) ? null : join(' ', array_map(function ($a) { return '[' . str_replace('$', '', trim($a)) . ']'; }, array_keys($args))); $results[$name] = compact('name', 'description', 'return', 'args', 'usage'); if ($name && $name == $options['name']) { return array($name => $results[$name]); } } return $results; }
/** * Tests that the range of executable lines of this test method is properly calculated. * Recursively meta. */ public function testMethodRange() { $result = Inspector::methods(__CLASS__, 'ranges', array('methods' => __FUNCTION__)); $expected = array(__FUNCTION__ => array(__LINE__ - 1, __LINE__, __LINE__ + 1)); $this->assertEqual($expected, $result); }
/** * Get the methods for the class. * * @param string $class * @param array $options * @return array */ protected function _methods($class, $options = array()) { $defaults = array('name' => null); $options += $defaults; $map = function ($item) { if ($item->name[0] === '_') { return; } $modifiers = array_values(Inspector::invokeMethod('_modifiers', array($item))); $setAccess = array_intersect($modifiers, array('private', 'protected')) != array(); if ($setAccess) { $item->setAccessible(true); } $args = array(); foreach ($item->getParameters() as $arg) { $args[] = array('name' => $arg->getName(), 'optional' => $arg->isOptional(), 'description' => null); } $result = compact('modifiers', 'args') + array('docComment' => $item->getDocComment(), 'name' => $item->getName()); if ($setAccess) { $item->setAccessible(false); } return $result; }; $methods = Inspector::methods($class)->map($map, array('collect' => false)); $results = array(); foreach (array_filter($methods) as $method) { $comment = Docblock::comment($method['docComment']); $name = $method['name']; $description = trim($comment['description'] . PHP_EOL . $comment['text']); $args = $method['args']; $return = null; foreach ($args as &$arg) { if (isset($comment['tags']['params']['$' . $arg['name']])) { $arg['description'] = $comment['tags']['params']['$' . $arg['name']]['text']; } $arg['usage'] = $arg['optional'] ? "[<{$arg['name']}>]" : "<{$arg['name']}>"; } if (isset($comment['tags']['return'])) { $return = trim(strtok($comment['tags']['return'], ' ')); } $results[$name] = compact('name', 'description', 'return', 'args'); if ($name && $name == $options['name']) { return array($name => $results[$name]); } } return $results; }