/** * Resolves a class member, which is either a field, a class constant * or the `ClassName::class` syntax, which returns the class' literal. * * @param lang.XPClass $class * @param var[] $token A token as returned by `token_get_all()` * @param string $context * @return var */ protected function memberOf($class, $token, $context) { if (T_VARIABLE === $token[0]) { $field = $class->getField(substr($token[1], 1)); $m = $field->getModifiers(); if ($m & MODIFIER_PUBLIC) { return $field->get(null); } else { if ($m & MODIFIER_PROTECTED && $class->isAssignableFrom($context)) { return $field->setAccessible(true)->get(null); } else { if ($m & MODIFIER_PRIVATE && $class->getName() === $context) { return $field->setAccessible(true)->get(null); } else { throw new IllegalAccessException(sprintf('Cannot access %s field %s::$%s', implode(' ', Modifiers::namesOf($m)), $class->getName(), $field->getName())); } } } } else { if (T_CLASS === $token[0]) { return $class->literal(); } else { return $class->getConstant($token[1]); } } }
/** * Asserts given modifiers do not contain abstract * * @param int modifiers * @throws unittest.AssertionFailedError */ protected function assertNotAbstract($modifiers) { $this->assertFalse(Modifiers::isAbstract($modifiers), implode(' | ', Modifiers::namesOf($modifiers))); }
/** * Prints package * * @param lang.reflect.Package package */ protected static function printPackage($package) { Console::writeLine('package ', $package->getName(), ' {'); // Child packages foreach ($package->getPackages() as $child) { Console::writeLine(' package ', $child->getName()); } // Classes $order = array('interface' => array(), 'enum' => array(), 'class' => array()); foreach ($package->getClasses() as $class) { $mod = $class->getModifiers(); if ($class->isInterface()) { $type = 'interface'; $mod = $mod ^ MODIFIER_ABSTRACT; } else { if ($class->isEnum()) { $type = 'enum'; } else { $type = 'class'; } } $name = self::displayNameOf($class); $order[$type][] = implode(' ', Modifiers::namesOf($mod)) . ' ' . $type . ' ' . self::displayNameOf($class); } foreach ($order as $type => $classes) { if (empty($classes)) { continue; } Console::writeLine(); sort($classes); foreach ($classes as $name) { Console::writeLine(' ', $name); } } Console::writeLine('}'); }
/** * Parses annotation string * * @param string input * @param string context the class name * @return [:string] imports * @param int line * @return [:var] * @throws lang.ClassFormatException */ public static function parseAnnotations($input, $context, $imports = array(), $line = -1) { static $states = array('annotation', 'annotation name', 'annotation value', 'annotation map key', 'annotation map value', 'multi-value'); $tokens = token_get_all('<?php ' . trim($input, "[# \t\n\r")); $annotations = array(0 => array(), 1 => array()); $place = $context . (-1 === $line ? '' : ', line ' . $line); // Resolve classes $resolve = function ($type, $context, $imports) { if ('self' === $type) { return XPClass::forName($context); } else { if ('parent' === $type) { return XPClass::forName($context)->getParentclass(); } else { if (FALSE !== strpos($type, '.')) { return XPClass::forName($type); } else { if (isset($imports[$type])) { return XPClass::forName($imports[$type]); } else { if (isset(xp::$cn[$type])) { return XPClass::forName(xp::$cn[$type]); } else { if (FALSE !== ($p = strrpos($context, '.'))) { return XPClass::forName(substr($context, 0, $p + 1) . $type); } else { return XPClass::forName($type); } } } } } } }; // Class::CONSTANT vs. Class::$MEMBER $memberOf = function ($tokens, &$i, $class) use($context) { if (T_VARIABLE === $tokens[$i][0]) { $field = $class->getField(substr($tokens[$i][1], 1)); $m = $field->getModifiers(); if ($m & MODIFIER_PUBLIC) { return $field->get(NULL); } else { if ($m & MODIFIER_PROTECTED && $class->isAssignableFrom($context)) { return $field->setAccessible(TRUE)->get(NULL); } else { if ($m & MODIFIER_PRIVATE && $class->getName() === $context) { return $field->setAccessible(TRUE)->get(NULL); } else { throw new IllegalAccessException(sprintf('Cannot access %s field %s::$%s', implode(' ', Modifiers::namesOf($m)), $class->getName(), $field->getName())); } } } } else { return $class->getConstant($tokens[$i][1]); } }; // Parse a single value (recursively, if necessary) $valueOf = function ($tokens, &$i) use(&$valueOf, &$memberOf, &$resolve, $context, $imports) { if ('-' === $tokens[$i][0]) { $i++; return -1 * $valueOf($tokens, $i); } else { if ('+' === $tokens[$i][0]) { $i++; return +1 * $valueOf($tokens, $i); } else { if (T_CONSTANT_ENCAPSED_STRING === $tokens[$i][0]) { return eval('return ' . $tokens[$i][1] . ';'); } else { if (T_LNUMBER === $tokens[$i][0]) { return (int) $tokens[$i][1]; } else { if (T_DNUMBER === $tokens[$i][0]) { return (double) $tokens[$i][1]; } else { if ('[' === $tokens[$i] || T_ARRAY === $tokens[$i][0]) { $value = array(); $element = NULL; $key = 0; $end = '[' === $tokens[$i] ? ']' : ')'; for ($i++, $s = sizeof($tokens);; $i++) { if ($i >= $s) { throw new IllegalStateException('Parse error: Unterminated array'); } else { if ($end === $tokens[$i]) { $element && ($value[$key] = $element[0]); break; } else { if ('(' === $tokens[$i]) { // Skip } else { if (',' === $tokens[$i]) { $element || raise('lang.IllegalStateException', 'Parse error: Malformed array - no value before comma'); $value[$key] = $element[0]; $element = NULL; $key = sizeof($value); } else { if (T_DOUBLE_ARROW === $tokens[$i][0]) { $key = $element[0]; $element = NULL; } else { if (T_WHITESPACE === $tokens[$i][0]) { continue; } else { $element && raise('lang.IllegalStateException', 'Parse error: Malformed array - missing comma'); $element = array($valueOf($tokens, $i)); } } } } } } } return $value; } else { if ('"' === $tokens[$i] || T_ENCAPSED_AND_WHITESPACE === $tokens[$i][0]) { throw new IllegalStateException('Parse error: Unterminated string'); } else { if (T_NS_SEPARATOR === $tokens[$i][0]) { $type = ''; while (T_NS_SEPARATOR === $tokens[$i++][0]) { $type .= '.' . $tokens[$i++][1]; } return $memberOf($tokens, $i, XPClass::forName(substr($type, 1))); } else { if (T_STRING === $tokens[$i][0]) { // constant vs. class::constant if (T_DOUBLE_COLON === $tokens[$i + 1][0]) { $i += 2; return $memberOf($tokens, $i, $resolve($tokens[$i - 2][1], $context, $imports)); } else { if (defined($tokens[$i][1])) { return constant($tokens[$i][1]); } else { raise('lang.ElementNotFoundException', 'Undefined constant "' . $tokens[$i][1] . '"'); } } } else { if (T_NEW === $tokens[$i][0]) { $type = ''; while ('(' !== $tokens[$i++]) { if (T_STRING === $tokens[$i][0]) { $type .= '.' . $tokens[$i][1]; } } $class = $resolve(substr($type, 1), $context, $imports); for ($args = array(), $arg = NULL, $s = sizeof($tokens);; $i++) { if (')' === $tokens[$i]) { $arg && ($args[] = $arg[0]); break; } else { if (',' === $tokens[$i]) { $args[] = $arg[0]; $arg = NULL; } else { if (T_WHITESPACE !== $tokens[$i][0]) { $arg = array($valueOf($tokens, $i)); } } } } return $class->hasConstructor() ? $class->getConstructor()->newInstance($args) : $class->newInstance(); } else { throw new IllegalStateException(sprintf('Parse error: Unexpected %s', is_array($tokens[$i]) ? token_name($tokens[$i][0]) : '"' . $tokens[$i] . '"')); } } } } } } } } } } }; // Parse tokens try { for ($state = 0, $i = 1, $s = sizeof($tokens); $i < $s; $i++) { if (T_WHITESPACE === $tokens[$i][0]) { continue; } else { if (0 === $state) { // Initial state, expecting @attr or @$param: attr if ('@' === $tokens[$i]) { $annotation = $tokens[$i + 1][1]; $param = NULL; $value = NULL; $i++; $state = 1; } else { throw new IllegalStateException('Parse error: Expecting "@"'); } } else { if (1 === $state) { // Inside attribute, check for values if ('(' === $tokens[$i]) { $state = 2; } else { if (',' === $tokens[$i]) { if ($param) { $annotations[1][$param][$annotation] = $value; } else { $annotations[0][$annotation] = $value; } $state = 0; } else { if (']' === $tokens[$i]) { if ($param) { $annotations[1][$param][$annotation] = $value; } else { $annotations[0][$annotation] = $value; } return $annotations; } else { if (':' === $tokens[$i]) { $param = $annotation; $annotation = NULL; } else { if (T_STRING === $tokens[$i][0]) { $annotation = $tokens[$i][1]; } else { throw new IllegalStateException('Parse error: Expecting either "(", "," or "]"'); } } } } } } else { if (2 === $state) { // Inside braces of @attr(...) if (')' === $tokens[$i]) { $state = 1; } else { if (',' === $tokens[$i]) { trigger_error('Deprecated usage of multi-value annotations in ' . $place, E_USER_DEPRECATED); $value = (array) $value; $state = 5; } else { if ($i + 2 < $s && ('=' === $tokens[$i + 1] || '=' === $tokens[$i + 2])) { $key = $tokens[$i][1]; $value = array(); $state = 3; } else { $value = $valueOf($tokens, $i); } } } } else { if (3 === $state) { // Parsing key inside @attr(a= b, c= d) if (')' === $tokens[$i]) { $state = 1; } else { if (',' === $tokens[$i]) { $key = null; } else { if ('=' === $tokens[$i]) { $state = 4; } else { if (is_array($tokens[$i])) { $key = $tokens[$i][1]; } } } } } else { if (4 === $state) { // Parsing value inside @attr(a= b, c= d) $value[$key] = $valueOf($tokens, $i); $state = 3; } else { if (5 === $state) { if (')' === $tokens[$i]) { // BC: Deprecated multi-value annotations $value[] = $element; $state = 1; } else { if (',' === $tokens[$i]) { $value[] = $element; } else { $element = $valueOf($tokens, $i); } } } } } } } } } } } catch (XPException $e) { raise('lang.ClassFormatException', $e->getMessage() . ' in ' . $place, $e); } raise('lang.ClassFormatException', 'Parse error: Unterminated ' . $states[$state] . ' in ' . $place); }