/** * Parse a class from token reader * * @param \vc\Tokens\Access $access The token access * @return \vc\Data\Routine\Func */ public function parseClass(\vc\Tokens\Access $access) { $token = $access->findRequired(array(Token::T_CLASS, Token::T_ABSTRACT)); $class = new \vc\Data\Type\Cls($token->getLine(), $access->getComment()); // Extract the abstract flag from the class definition if ($token->is(Token::T_ABSTRACT)) { $class->setAbstract(TRUE); $access->findRequired(array(Token::T_CLASS)); } // Searches for the name of the class $token = $access->findRequired(array(Token::T_STRING)); // Set the name of the class $class->setName($token->getContent()); // Look for parent classes and interfaces $token = $access->findRequired(array(Token::T_EXTENDS, Token::T_IMPLEMENTS, Token::T_CURLY_OPEN)); // Add the parent class if ($token->is(Token::T_EXTENDS)) { $class->setExtends($this->path->parsePath($access)); // Look for any interfaces $token = $access->findRequired(array(Token::T_IMPLEMENTS, Token::T_CURLY_OPEN)); } // Add any interface implementations if ($token->is(Token::T_IMPLEMENTS)) { $class->setIFaces($this->pathList->parsePathList($access)); $access->findRequired(array(Token::T_CURLY_OPEN)); } // Finally, parse out the content of the class $this->members->parseMembers($class, $access); return $class; }
/** * Parses the given token reader * * @param \vc\Data\Signature $signature The signature to use as a source * @param \vc\Tokens\Access $access The token access * @return \vc\Data\Property */ public function parseProperty(\vc\Data\Signature $signature, \vc\Tokens\Access $access) { $prop = $signature->buildProperty(); // Keep looking for modifier tokens until we reach the variable. // This isn't as strict as it could be about token order, but it greatly // simplifies the method to do it like this. do { $token = $access->findRequired(array(Token::T_STATIC, Token::T_VAR, Token::T_VARIABLE, Token::T_PUBLIC, Token::T_PROTECTED, Token::T_PRIVATE)); if ($token->is(array(Token::T_PUBLIC, Token::T_PROTECTED, Token::T_PRIVATE))) { $prop->setVisibility(\vc\Data\Visibility::fromToken($token)); } else { if ($token->is(Token::T_STATIC)) { $prop->setStatic(TRUE); } } } while (!$token->is(Token::T_VARIABLE)); $prop->setName($token->getContent()); $token = $access->findRequired(array(Token::T_SEMICOLON, Token::T_EQUALS)); // Look for any default value if ($token->is(Token::T_EQUALS)) { $prop->setValue($this->value->parseValue($access)); $access->findRequired(array(Token::T_SEMICOLON)); } return $prop; }
/** * Parses the given token reader * * @param \vc\Data\Object\Cls $class The class to fill with data * @param \vc\Tokens\Access $access The token access * @return NULL */ public function parseSignature(\vc\Data\Type\Cls $class, \vc\Tokens\Access $access) { $sig = new \vc\Data\Signature($access->peekAtToken()->getLine(), $access->getComment()); // Keep collecting tokens until we find one that differentiates this // signature between a method and a property. This isn't as strict // as it could be, but it's already complex while (TRUE) { $token = $access->peekToRequired(array(Token::T_STATIC, Token::T_PUBLIC, Token::T_PROTECTED, Token::T_PRIVATE, Token::T_ABSTRACT, Token::T_FINAL, Token::T_FUNCTION, Token::T_VAR, Token::T_VARIABLE)); // These tokens denote a definite method if ($token->is(array(Token::T_ABSTRACT, Token::T_FINAL, Token::T_FUNCTION))) { $class->addMethod($this->methods->parseMethod($sig, $access)); return; } else { if ($token->is(array(Token::T_VAR, Token::T_VARIABLE))) { $class->addProperty($this->properties->parseProperty($sig, $access)); return; } else { if ($token->is(array(Token::T_PUBLIC, Token::T_PROTECTED, Token::T_PRIVATE))) { $sig->setVisibility(\vc\Data\Visibility::fromToken($token)); } else { if ($token->is(Token::T_STATIC)) { $sig->setStatic(TRUE); } } } } // By this point, we have already determined that this token isn't // needed downstream, so we can pop it safely $access->popToken(); } }
/** * Parses the given token reader * * @param \vc\Data\Signature $signature The base data from which to build * the method * @param \vc\Tokens\Access $access The token stream * @return \vc\Data\Routine\Method */ public function parseMethod(\vc\Data\Signature $signature, \vc\Tokens\Access $access) { $method = $signature->buildMethod(); // Continue iterating until we encounter a Function token while (TRUE) { $token = $access->peekToRequired(array(Token::T_STATIC, Token::T_ABSTRACT, Token::T_FINAL, Token::T_PUBLIC, Token::T_PROTECTED, Token::T_PRIVATE, Token::T_FUNCTION)); // We don't want to consume the function token because the routine // parser looks for it. Break before we get a chance to pop it off // the stream if ($token->is(Token::T_FUNCTION)) { break; } $access->popToken(); if ($token->is(array(Token::T_PUBLIC, Token::T_PROTECTED, Token::T_PRIVATE))) { $method->setVisibility(\vc\Data\Visibility::fromToken($token)); } else { if ($token->is(Token::T_STATIC)) { $method->setStatic(TRUE); } else { if ($token->is(Token::T_ABSTRACT)) { $method->setAbstract(TRUE); } else { if ($token->is(Token::T_FINAL)) { $method->setFinal(TRUE); } } } } } $this->routine->parseRoutine($method, $access); return $method; }
/** * Parse a function out of the given token reader * * @param \vc\Tokens\Access $access The token access * @return \vc\Data\Routine\Func */ public function parseFunc(\vc\Tokens\Access $access) { $token = $access->peekToRequired(array(Token::T_FUNCTION)); $func = new \vc\Data\Routine\Func($token->getLine(), $access->getComment()); $this->routine->parseRoutine($func, $access); return $func; }
/** * Parses the given token reader * * @param \vc\Tokens\Access $access The token access * @return \vc\Data\Constant Returns the created constant */ public function parseConstant(\vc\Tokens\Access $access) { $access->findRequired(array(Token::T_CONST)); $name = $access->findRequired(array(Token::T_STRING)); $const = new \vc\Data\Constant($name->getContent()); $access->findRequired(array(Token::T_EQUALS)); $const->setValue($this->value->parseValue($access)); $access->findRequired(array(Token::T_SEMICOLON)); return $const; }
/** * Parses the given token reader * * @param \vc\Tokens\Access $access The token access * @return \vc\Data\Alias Returns the created alias */ public function parseAlias(\vc\Tokens\Access $access) { $access->findRequired(array(Token::T_USE)); $alias = new \vc\Data\Alias($this->path->parsePath($access)); $as = $access->find(array(Token::T_AS)); if ($as) { $alias->setAlias($this->path->parsePath($access)); } $access->findRequired(array(Token::T_SEMICOLON)); return $alias; }
public function testParseProperty_defaultValue() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAPublic->thenSomeSpace->thenAVariable('$var')->thenSomeSpace->thenAnEquals->thenAnInteger(789)->thenASemicolon); $sig = new \vc\Data\Signature(120, new \vc\Data\Comment('Note')); $this->assertEquals(r8(new \vc\Data\Property(120, new \vc\Data\Comment('Note')))->setName('$var')->setValue(new \vc\Data\Value(789, 'int')), $this->getPropertyParser()->parseProperty($sig, $access)); $this->assertEndOfTokens($access); }
public function testParseConstant_Heredoc() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAConst->thenSomeSpace->thenAName('NAME')->thenSomeSpace->thenAnEquals->thenSomeSpace->thenAHereDoc('contents')->thenASemicolon->thenAClass); $parser = $this->getConstantParser(); $this->assertEquals(r8(new \vc\Data\Constant('NAME'))->setValue(new \vc\Data\Value('contents', 'string')), $parser->parseConstant($access)); $this->assertHasToken(Token::T_CLASS, $access); }
/** * Parses the given token reader * * @param \r8\FileSys\File $path The file to parse * @return \vc\Data\File */ public function parse(\r8\FileSys\File $path) { $file = new \vc\Data\File($path->getPath()); $tokens = \vc\Tokens\Access::buildAccess(new \vc\Tokens\Parser(new \r8\Stream\In\File($path))); $this->comment->parse($file, $tokens); return $file; }
public function testUntilBlockEnds() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAFunction->thenACloseBlock->thenAClass); $until = $access->untilBlockEnds(); $this->assertThat($until, $this->isInstanceOf('\\vc\\Tokens\\Access')); $this->assertHasToken(Token::T_FUNCTION, $until); $this->assertEndOfTokens($until); }
public function testNoCommentFound() { $reader = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAnOpenTag->thenSomeSpace->thenAClass->thenADocComment('This is a file comment')); $parser = new \vc\Parser\File\Comment(new \vc\Parser\Comment(), $this->getStub('vc\\Parser\\File\\NSpaces')); $file = new \vc\Data\File('file.php'); $parser->parse($file, $reader); $this->assertEquals(new \vc\Data\Comment(), $file->getComment()); }
public function testParseAnonymousFunc_ReturnsReference() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAFunction->thenSomeSpace->thenAnAmpersand->thenOpenParens->thenCloseParens->thenASemicolon); $routine = $this->getMockForAbstractClass('\\vc\\Data\\Routine', array(1)); $this->getFuncParser()->parseRoutine($routine, $access); $this->assertNull($routine->getName()); $this->assertTrue($routine->getReturnRef()); $this->assertEndOfTokens($access); }
public function testParseIFace_Constant() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAConst->thenAName('CONST')->thenAnEquals->thenAnInteger(123)->thenASemicolon); $iface = new \vc\Data\Type\IFace(1); $parser = new \vc\Parser\IFace\Members(new \vc\Parser\Constant(new \vc\Parser\Value(new \vc\Parser\Brackets(), new \vc\Parser\Path())), $this->getStub('\\vc\\Parser\\Routine\\Method')); $parser->parseMembers($iface, $access); $this->assertEquals(1, count($iface->getConstants())); $this->assertEndOfTokens($access); }
public function testParse_MultipleSemicolonNamespaceDefinitions() { $file = new \vc\Data\File('path.php'); $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenANamespace('sub\\sub2')->thenASemicolon->thenAFunction->thenANamespace('ns2')->thenASemicolon->thenAFunction); $parser = new \vc\Parser\File\NSpaces(new \vc\Parser\Path(), $this->getNSpaceParser(2)); $parser->parse($file, $access); $this->assertEquals(array(new \vc\Data\NSpace('sub\\sub2'), new \vc\Data\NSpace('ns2')), $file->getNamespaces()); $this->assertEndOfTokens($access); }
public function testParseAlias_MissingSemicolon() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAUse->thenSomeSpace->thenANamespacePath('sub1\\sub2')->thenSomeSpace->then(Token::T_AS, 'as')->thenSomeSpace->thenAName('renamed')); $parser = new \vc\Parser\NSpace\Alias(new \vc\Parser\Path()); try { $parser->parseAlias($access); $this->fail("An expected exception was not thrown"); } catch (\vc\Tokens\Exception\UnexpectedEnd $err) { } }
public function testParseConstant() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAConst); $constant = new \vc\Data\Constant('CONST'); $constParser = $this->getStub('\\vc\\Parser\\Constant'); $constParser->expects($this->once())->method("parseConstant")->with($this->isInstanceOf('\\vc\\Tokens\\Access'))->will($this->returnCallback(function ($access) use($constant) { $access->popToken(); return $constant; })); $sigParser = $this->getStub('\\vc\\Parser\\Object\\Signature'); $sigParser->expects($this->never())->method("parseSignature"); $class = new \vc\Data\Type\Cls(1); $parser = new \vc\Parser\Object\Members($constParser, $sigParser); $parser->parseMembers($class, $access); $this->assertSame(array($constant), $class->getConstants()); $this->assertEndOfTokens($access); }
/** * Parse a function out of the given token reader * * @param \vc\Data\Routine $routine The object to fill with data * @param \vc\Tokens\Access $access The token access * @return NULL */ public function parseRoutine(\vc\Data\Routine $routine, \vc\Tokens\Access $access) { $access->findRequired(array(Token::T_FUNCTION)); $token = $access->peekToRequired(array(Token::T_STRING, Token::T_AMPERSAND, Token::T_PARENS_OPEN)); // Handle routines that return a reference if ($token->is(Token::T_AMPERSAND)) { $routine->setReturnRef(TRUE); $access->popToken(); $token = $access->peekToRequired(array(Token::T_STRING, Token::T_PARENS_OPEN)); } // Names are optional because of anonymous methods if ($token->is(Token::T_STRING)) { $access->popToken(); $routine->setName($token->getContent()); } $routine->setArgs($this->args->parseArgs($access)); $access->findRequired(array(Token::T_CURLY_OPEN, Token::T_SEMICOLON)); $this->brackets->parseCurlies($access); }
/** * Parse an interface from token reader * * @param \vc\Tokens\Access $access The token access * @return \vc\Data\Routine\Func */ public function parseIFace(\vc\Tokens\Access $access) { $token = $access->findRequired(array(Token::T_INTERFACE)); $iface = new \vc\Data\Type\IFace($token->getLine(), $access->getComment()); // Searches for the name of the interface $token = $access->findRequired(array(Token::T_STRING)); $iface->setName($token->getContent()); // Look for any interfaces that this one extends $token = $access->findRequired(array(Token::T_EXTENDS, Token::T_CURLY_OPEN)); if ($token->is(Token::T_EXTENDS)) { $iface->setExtends($this->pathList->parsePathList($access)); $access->findRequired(array(Token::T_CURLY_OPEN)); } // Finally, parse out the content of the class $this->members->parseMembers($iface, $access); return $iface; }
/** * Parses the given token reader * * @param \vc\Data\NSpace $nspace The namespace to parse data into * @param \vc\Tokens\Search $access The token access * @return NULL */ public function parseNSpace(\vc\Data\NSpace $nspace, \vc\Tokens\Access $access) { $last = NULL; // Keep looking until we have consumed all the tokens in this namespace while (TRUE) { $token = $access->peekToSkipping(array(Token::T_CLASS, Token::T_ABSTRACT, Token::T_CONST, Token::T_INTERFACE, Token::T_FUNCTION, Token::T_USE)); if (!$token) { break; } if ($token === $last) { throw new \RuntimeException('Possible Infinite Loop Detected. ' . 'Current token has already been parsed'); } $last = $token; if ($token->is(array(Token::T_CLASS, Token::T_ABSTRACT))) { $nspace->addType($this->object->parseClass($access)); } else { if ($token->is(Token::T_FUNCTION)) { $func = $this->func->parseFunc($access); // Anonymous functions have a very limited scope, so we don't // care about documenting them if (!$func->isAnonymous()) { $nspace->addFunction($func); } } else { if ($token->is(Token::T_INTERFACE)) { $nspace->addType($this->iface->parseIFace($access)); } else { if ($token->is(Token::T_CONST)) { $nspace->addConstant($this->constant->parseConstant($access)); } else { if ($token->is(Token::T_USE)) { $nspace->addAlias($this->alias->parseAlias($access)); } } } } } } }
public function testParseNSpace_Many() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAnInterface->thenAClass->thenAConst->thenAFunction); $nspace = new \vc\Data\NSpace(1); $this->getNSpaceParser()->parseNSpace($nspace, $access); $this->assertEquals(2, count($nspace->getTypes())); $this->assertEquals(1, count($nspace->getConstants())); $this->assertEquals(1, count($nspace->getFunctions())); $this->assertEndOfTokens($access); }
public function testParsePathList_ThreePaths() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenSomeSpace->thenANamespacePath('path\\to\\cls')->thenAComma->thenSomeSpace->thenANamespacePath('AnObject')->thenAComma->thenSomeSpace->thenANamespacePath('\\stdClass')->thenSomeSpace->thenAnOpenBlock); $this->assertEquals(array('path\\to\\cls', 'AnObject', '\\stdClass'), $this->getPathListParser()->parsePathList($access)); }
/** * Parses a list of method arguments * * @param \vc\Tokens\Access $access * @return Array */ public function parseArgs(\vc\Tokens\Access $access) { $access->findRequired(array(Token::T_PARENS_OPEN)); $args = array(); // Continue parsing until we hit a close parenthesis while (TRUE) { $type = $access->peekToRequired(array(Token::T_ARRAY, Token::T_STRING, Token::T_NS_SEPARATOR, Token::T_AMPERSAND, Token::T_VARIABLE, Token::T_PARENS_CLOSE)); if ($type->is(Token::T_PARENS_CLOSE)) { break; } $args[] = $this->parseArg($access); } $access->popToken(); return $args; }
/** * Parses the given token reader * * @param \vc\Data\File $file The file to parse data into * @param \vc\Tokens\Access $access The token access * @return NULL */ public function parse(\vc\Data\File $file, \vc\Tokens\Access $access) { while ($access->hasToken()) { $nspace = new \vc\Data\NSpace(); // If we can't find a namespace, then we are in the global scope if ($this->findNS($access)) { $inner = $this->buildNS($nspace, $access); } else { $inner = $access->untilTokens(array(Token::T_NAMESPACE)); } $file->addNamespace($nspace); $this->nspace->parseNSpace($nspace, $inner); } }
public function testParseMethod_AbstractPrivateMethod() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenAnAbstract->thenSomeSpace->thenAPrivate->thenSomeSpace->thenAFunction->thenSomeSpace->thenAName('MyFunc')->thenOpenParens->thenCloseParens->thenAnOpenBlock->thenACloseBlock); $sig = new \vc\Data\Signature(123); $this->assertEquals(r8(new \vc\Data\Routine\Method(123))->setName('MyFunc')->setAbstract(TRUE)->setVisibility(\vc\Data\Visibility::vPrivate()), $this->getMethodParser()->parseMethod($sig, $access)); }
public function testParseArgs_Combined() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenOpenParens->thenANamespacePath('\\path\\to\\class')->thenSomeSpace->thenAnAmpersand->thenSomeSpace->thenAVariable('$test')->thenSomeSpace->thenAnEquals->thenSomeSpace->thenAnInteger(123)->thenAComma->thenAnArray->thenSomeSpace->thenAnAmpersand->thenSomeSpace->thenAVariable('$test2')->thenSomeSpace->thenAnEquals->thenSomeSpace->thenAnArrayValue(array(1))->thenCloseParens); $this->assertEquals(array(\r8(new \vc\Data\Arg())->setType('\\path\\to\\class')->setReference(TRUE)->setVariable('$test')->setDefault(new \vc\Data\Value(123, 'int')), \r8(new \vc\Data\Arg())->setType('array')->setReference(TRUE)->setVariable('$test2')->setDefault(new \vc\Data\Value('array( 0 => 1,)', 'array'))), $this->getArgParser()->parseArgs($access)); $this->assertEndOfTokens($access); }
/** * Parses a value from a token stream * * @param \vc\Tokens\Access $access * @return \vc\Data\Value */ public function parseValue(\vc\Tokens\Access $access) { $token = $access->peekToRequired(array(Token::T_LNUMBER, Token::T_DNUMBER, Token::T_STRING, Token::T_START_HEREDOC, Token::T_CONSTANT_ENCAPSED_STRING, Token::T_ARRAY, Token::T_MINUS, Token::T_NS_SEPARATOR), array(Token::T_EQUALS)); switch ($token->getType()) { // Strings case Token::T_CONSTANT_ENCAPSED_STRING: $access->popToken(); return $this->parseString($token); // HereDocs // HereDocs case Token::T_START_HEREDOC: $access->popToken(); return $this->parseHereDoc($access); // Negative numbers // Negative numbers case Token::T_MINUS: $access->popToken(); $token = $access->findRequired(array(Token::T_LNUMBER, Token::T_DNUMBER)); return $this->parseNumber(FALSE, $token); // Positive Numbers // Positive Numbers case Token::T_LNUMBER: case Token::T_DNUMBER: $access->popToken(); return $this->parseNumber(TRUE, $token); // Keywords like true, false, null and relative constants // Keywords like true, false, null and relative constants case Token::T_STRING: return $this->parseKeyword($access, $token); // Absolute Constants // Absolute Constants case Token::T_NS_SEPARATOR: return $this->parseConstant($access); // Arrays // Arrays case Token::T_ARRAY: $access->popToken(); return $this->parseArray($access); default: throw new \RuntimeException("Unexpected Type"); } }
public function testParseValue_ClassConstant() { $access = \vc\Tokens\Access::buildAccess($this->oneTokenReader()->thenSomeSpace->thenAnEquals->thenSomeSpace->thenANamespacePath('\\path')->then(Token::T_DOUBLE_COLON, '::')->thenAName('CONSTANT')); $parser = $this->getValueParser(); $this->assertEquals(new \vc\Data\Value('\\path::CONSTANT', 'constant'), $parser->parseValue($access)); $this->assertEndOfTokens($access); }