diff --git a/.travis.yml b/.travis.yml index e686d783..e5bdfacd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,16 @@ language: php php: - - 5.3.3 - - 5.4 - - 5.5 - 5.6 - 7.0 - - hhvm matrix: - allow_failures: - - php: 7.0 - - php: hhvm + include: + - php: 5.6 + env: dependencies="--prefer-lowest --prefer-stable" script: - - vendor/bin/tester tests -s -p php - - php code-checker/src/code-checker.php + - vendor/bin/tester tests -s $coverage + - php temp/code-checker/src/code-checker.php --short-arrays after_failure: # Print *.actual content @@ -22,5 +18,20 @@ after_failure: before_script: # Install Nette Tester & Code Checker - - composer install --no-interaction --prefer-source - - composer create-project nette/code-checker code-checker ~2.3 --no-interaction --prefer-source + - travis_retry composer update --no-interaction --prefer-dist $dependencies + - travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction + - if [ $TRAVIS_PHP_VERSION == "7.0" ]; then coverage="-p phpdbg --coverage ./coverage.xml --coverage-src ./src"; fi + +after_script: + # Report Code Coverage + - > + if [ "$coverage" != "" ]; then + wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar + && php coveralls.phar --verbose --config tests/.coveralls.yml + || true; fi + +sudo: false + +cache: + directories: + - $HOME/.composer/cache diff --git a/composer.json b/composer.json index a61bd64a..de69ad90 100644 --- a/composer.json +++ b/composer.json @@ -1,24 +1,25 @@ { "name": "nette/php-generator", "description": "Nette PHP Generator", - "homepage": "http://nette.org", + "homepage": "https://nette.org", "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"], "authors": [ { "name": "David Grudl", - "homepage": "http://davidgrudl.com" + "homepage": "https://davidgrudl.com" }, { "name": "Nette Community", - "homepage": "http://nette.org/contributors" + "homepage": "https://nette.org/contributors" } ], "require": { - "php": ">=5.3.1", - "nette/utils": "~2.2" + "php": ">=5.6.0", + "nette/utils": "~2.4" }, "require-dev": { - "nette/tester": "~1.0" + "nette/tester": "~2.0", + "tracy/tracy": "^2.3" }, "conflict": { "nette/nette": "<2.2" @@ -29,7 +30,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4-dev" } } } diff --git a/contributing.md b/contributing.md index a1cbbd53..860882bf 100644 --- a/contributing.md +++ b/contributing.md @@ -5,7 +5,7 @@ The issue tracker is the preferred channel for bug reports, features requests and submitting pull requests, but please respect the following restrictions: * Please **do not** use the issue tracker for personal support requests (use - [Nette forum](http://forum.nette.org) or [Stack Overflow](http://stackoverflow.com)). + [Nette forum](https://forum.nette.org) or [Stack Overflow](http://stackoverflow.com)). * Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others. @@ -21,7 +21,7 @@ fits with the scope and aims of the project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Nette welcomes **pull requests**. If you'd like to contribute, please take a moment -to [read the guidelines](http://nette.org/en/contributing) in order to make +to [read the guidelines](https://nette.org/en/contributing) in order to make the contribution process easy and effective for everyone involved. Thanks! diff --git a/license.md b/license.md index af571d59..cf741bd0 100644 --- a/license.md +++ b/license.md @@ -21,7 +21,7 @@ If your stuff is good, it will not take long to establish a reputation for yours New BSD License --------------- -Copyright (c) 2004, 2014 David Grudl (http://davidgrudl.com) +Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/readme.md b/readme.md index a34ac641..e9307e03 100644 --- a/readme.md +++ b/readme.md @@ -3,6 +3,9 @@ Nette PHP Generator [![Downloads this Month](https://img.shields.io/packagist/dm/nette/php-generator.svg)](https://packagist.org/packages/nette/php-generator) [![Build Status](https://travis-ci.org/nette/php-generator.svg?branch=master)](https://travis-ci.org/nette/php-generator) +[![Coverage Status](https://coveralls.io/repos/github/nette/php-generator/badge.svg?branch=master)](https://coveralls.io/github/nette/php-generator?branch=master) +[![Latest Stable Version](https://poser.pugx.org/nette/php-generator/v/stable)](https://github.com/nette/php-generator/releases) +[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/php-generator/blob/master/license.md) Generate PHP code with a simple programmatical API. diff --git a/src/PhpGenerator/ClassType.php b/src/PhpGenerator/ClassType.php index 43939a9a..b657638b 100644 --- a/src/PhpGenerator/ClassType.php +++ b/src/PhpGenerator/ClassType.php @@ -1,30 +1,32 @@ value */ - private $consts = array(); + private $consts = []; /** @var Property[] name => Property */ - private $properties = array(); + private $properties = []; /** @var Method[] name => Method */ - private $methods = array(); + private $methods = []; /** @@ -68,40 +70,35 @@ class ClassType extends Nette\Object public static function from($from) { $from = $from instanceof \ReflectionClass ? $from : new \ReflectionClass($from); - $class = new static($from->getShortName()); - $class->type = $from->isInterface() ? 'interface' : (PHP_VERSION_ID >= 50400 && $from->isTrait() ? 'trait' : 'class'); + if (PHP_VERSION_ID >= 70000 && $from->isAnonymous()) { + $class = new static('anonymous'); + } else { + $class = new static($from->getShortName(), new PhpNamespace($from->getNamespaceName())); + } + $class->type = $from->isInterface() ? 'interface' : ($from->isTrait() ? 'trait' : 'class'); $class->final = $from->isFinal() && $class->type === 'class'; $class->abstract = $from->isAbstract() && $class->type === 'class'; $class->implements = $from->getInterfaceNames(); - $class->documents = preg_replace('#^\s*\* ?#m', '', trim($from->getDocComment(), "/* \r\n\t")); - $namespace = $from->getNamespaceName(); + $class->comment = $from->getDocComment() ? preg_replace('#^\s*\* ?#m', '', trim($from->getDocComment(), "/* \r\n\t")) : NULL; if ($from->getParentClass()) { $class->extends = $from->getParentClass()->getName(); - if ($namespace) { - $class->extends = Strings::startsWith($class->extends, "$namespace\\") ? substr($class->extends, strlen($namespace) + 1) : '\\' . $class->extends; - } $class->implements = array_diff($class->implements, $from->getParentClass()->getInterfaceNames()); } - if ($namespace) { - foreach ($class->implements as & $interface) { - $interface = Strings::startsWith($interface, "$namespace\\") ? substr($interface, strlen($namespace) + 1) : '\\' . $interface; - } - } foreach ($from->getProperties() as $prop) { - if ($prop->getDeclaringClass() == $from) { // intentionally == + if ($prop->isDefault() && $prop->getDeclaringClass()->getName() === $from->getName()) { $class->properties[$prop->getName()] = Property::from($prop); } } foreach ($from->getMethods() as $method) { - if ($method->getDeclaringClass() == $from) { // intentionally == - $class->methods[$method->getName()] = Method::from($method); + if ($method->getDeclaringClass()->getName() === $from->getName()) { + $class->methods[$method->getName()] = Method::from($method)->setNamespace($class->namespace); } } return $class; } - public function __construct($name = NULL, PhpNamespace $namespace = NULL) + public function __construct($name = '', PhpNamespace $namespace = NULL) { $this->setName($name); $this->namespace = $namespace; @@ -113,65 +110,45 @@ public function __construct($name = NULL, PhpNamespace $namespace = NULL) */ public function __toString() { - $consts = array(); + $consts = []; foreach ($this->consts as $name => $value) { $consts[] = "const $name = " . Helpers::dump($value) . ";\n"; } - $properties = array(); + $properties = []; foreach ($this->properties as $property) { - $doc = str_replace("\n", "\n * ", implode("\n", (array) $property->getDocuments())); - $properties[] = ($property->getDocuments() ? (strpos($doc, "\n") === FALSE ? "/** $doc */\n" : "/**\n * $doc\n */\n") : '') + $doc = str_replace("\n", "\n * ", $property->getComment()); + $properties[] = ($doc ? (strpos($doc, "\n") === FALSE ? "/** $doc */\n" : "/**\n * $doc\n */\n") : '') . $property->getVisibility() . ($property->isStatic() ? ' static' : '') . ' $' . $property->getName() . ($property->value === NULL ? '' : ' = ' . Helpers::dump($property->value)) . ";\n"; } - $extends = $implements = $traits = array(); - if ($this->namespace) { - foreach ((array) $this->extends as $name) { - $extends[] = $this->namespace->unresolveName($name); - } - - foreach ((array) $this->implements as $name) { - $implements[] = $this->namespace->unresolveName($name); - } - - foreach ((array) $this->traits as $name) { - $traits[] = $this->namespace->unresolveName($name); - } - - } else { - $extends = (array) $this->extends; - $implements = (array) $this->implements; - $traits = (array) $this->traits; - } - - foreach ($this->methods as $method) { - $method->setNamespace($this->namespace); - } + $mapper = function (array $arr) { + return $this->namespace ? array_map([$this->namespace, 'unresolveName'], $arr) : $arr; + }; return Strings::normalize( - ($this->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $this->documents)) . "\n */\n" : '') + ($this->comment ? str_replace("\n", "\n * ", "/**\n" . $this->comment) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . $this->type . ' ' . $this->name . ' ' - . ($this->extends ? 'extends ' . implode(', ', $extends) . ' ' : '') - . ($this->implements ? 'implements ' . implode(', ', $implements) . ' ' : '') - . "\n{\n\n" + . ($this->extends ? 'extends ' . implode(', ', $mapper((array) $this->extends)) . ' ' : '') + . ($this->implements ? 'implements ' . implode(', ', $mapper($this->implements)) . ' ' : '') + . "\n{\n" . Strings::indent( - ($this->traits ? 'use ' . implode(', ', $traits) . ";\n\n" : '') - . ($this->consts ? implode('', $consts) . "\n\n" : '') - . ($this->properties ? implode("\n", $properties) . "\n\n" : '') - . implode("\n\n\n", $this->methods), 1) - . "\n\n}" + ($this->traits ? 'use ' . implode(";\nuse ", $mapper($this->traits)) . ";\n\n" : '') + . ($this->consts ? implode('', $consts) . "\n" : '') + . ($this->properties ? implode("\n", $properties) . "\n" : '') + . ($this->methods ? "\n" . implode("\n\n\n", $this->methods) . "\n\n" : ''), 1) + . '}' ) . "\n"; } /** - * @return PhpNamespace + * @return PhpNamespace|NULL */ public function getNamespace() { @@ -205,7 +182,7 @@ public function getName() */ public function setType($type) { - if (!in_array($type, array('class', 'interface', 'trait'), TRUE)) { + if (!in_array($type, ['class', 'interface', 'trait'], TRUE)) { throw new Nette\InvalidArgumentException('Argument must be class|interface|trait.'); } $this->type = $type; @@ -263,7 +240,7 @@ public function isAbstract() /** - * @param strings|string[] + * @param string|string[] * @return self */ public function setExtends($types) @@ -277,7 +254,7 @@ public function setExtends($types) /** - * @return strings|string[] + * @return string|string[] */ public function getExtends() { @@ -360,22 +337,22 @@ public function addTrait($trait) /** - * @param string[] + * @param string|NULL * @return self */ - public function setDocuments(array $s) + public function setComment($val) { - $this->documents = $s; + $this->comment = $val ? (string) $val : NULL; return $this; } /** - * @return string[] + * @return string|NULL */ - public function getDocuments() + public function getComment() { - return $this->documents; + return $this->comment; } @@ -383,13 +360,37 @@ public function getDocuments() * @param string * @return self */ - public function addDocument($s) + public function addComment($val) { - $this->documents[] = (string) $s; + $this->comment .= $this->comment ? "\n$val" : $val; return $this; } + /** @deprecated */ + public function setDocuments(array $s) + { + trigger_error(__METHOD__ . '() is deprecated, use similar setComment()', E_USER_DEPRECATED); + return $this->setComment(implode("\n", $s)); + } + + + /** @deprecated */ + public function getDocuments() + { + trigger_error(__METHOD__ . '() is deprecated, use similar getComment()', E_USER_DEPRECATED); + return $this->comment ? [$this->comment] : []; + } + + + /** @deprecated */ + public function addDocument($s) + { + trigger_error(__METHOD__ . '() is deprecated, use addComment()', E_USER_DEPRECATED); + return $this->addComment($s); + } + + /** * @return self */ @@ -427,12 +428,13 @@ public function addConst($name, $value) */ public function setProperties(array $props) { + $this->properties = []; foreach ($props as $v) { if (!$v instanceof Property) { throw new Nette\InvalidArgumentException('Argument must be Nette\PhpGenerator\Property[].'); } + $this->properties[$v->getName()] = $v; } - $this->properties = $props; return $this; } @@ -465,8 +467,7 @@ public function getProperty($name) */ public function addProperty($name, $value = NULL) { - $property = new Property; - return $this->properties[$name] = $property->setName($name)->setValue($value); + return $this->properties[$name] = (new Property($name))->setValue($value); } @@ -476,12 +477,13 @@ public function addProperty($name, $value = NULL) */ public function setMethods(array $methods) { + $this->methods = []; foreach ($methods as $v) { if (!$v instanceof Method) { throw new Nette\InvalidArgumentException('Argument must be Nette\PhpGenerator\Method[].'); } + $this->methods[$v->getName()] = $v->setNamespace($this->namespace); } - $this->methods = $methods; return $this; } @@ -513,13 +515,13 @@ public function getMethod($name) */ public function addMethod($name) { - $method = new Method; + $method = (new Method($name))->setNamespace($this->namespace); if ($this->type === 'interface') { $method->setVisibility(NULL)->setBody(FALSE); } else { $method->setVisibility('public'); } - return $this->methods[$name] = $method->setName($name); + return $this->methods[$name] = $method; } } diff --git a/src/PhpGenerator/Helpers.php b/src/PhpGenerator/Helpers.php index eb4e5798..398e3ce3 100644 --- a/src/PhpGenerator/Helpers.php +++ b/src/PhpGenerator/Helpers.php @@ -1,8 +1,8 @@ = 70000 && (new \ReflectionObject($var))->isAnonymous()) { + throw new Nette\InvalidArgumentException('Cannot dump anonymous class.'); + } $arr = (array) $var; $space = str_repeat("\t", $level); $class = get_class($var); - static $list = array(); + static $list = []; if ($level > self::MAX_DEPTH || in_array($var, $list, TRUE)) { throw new Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.'); @@ -114,15 +123,15 @@ private static function _dump(& $var, $level = 0) } foreach ($arr as $k => & $v) { if (!isset($props) || isset($props[$k])) { - $out .= "$space\t" . self::_dump($k, $level + 1) . " => " . self::_dump($v, $level + 1) . ",\n"; + $out .= "$space\t" . self::_dump($k, $level + 1) . ' => ' . self::_dump($v, $level + 1) . ",\n"; } } array_pop($list); $out .= $space; } return $class === 'stdClass' - ? "(object) array($out)" - : __CLASS__ . "::createObject('$class', array($out))"; + ? "(object) [$out]" + : __CLASS__ . "::createObject('$class', [$out])"; } elseif (is_resource($var)) { throw new Nette\InvalidArgumentException('Cannot dump resource.'); @@ -137,10 +146,9 @@ private static function _dump(& $var, $level = 0) * Generates PHP statement. * @return string */ - public static function format($statement) + public static function format($statement, ...$args) { - $args = func_get_args(); - return self::formatArgs(array_shift($args), $args); + return self::formatArgs($statement, $args); } @@ -170,7 +178,7 @@ public static function formatArgs($statement, array $args) $a = strlen($s); } else { - $arg = substr($statement, $a - 1, 1) === '$' || in_array(substr($statement, $a - 2, 2), array('->', '::'), TRUE) + $arg = substr($statement, $a - 1, 1) === '$' || in_array(substr($statement, $a - 2, 2), ['->', '::'], TRUE) ? self::formatMember($arg) : self::_dump($arg); $statement = substr_replace($statement, $arg, $a, 1); $a += strlen($arg); @@ -189,7 +197,7 @@ public static function formatMember($name) { return $name instanceof PhpLiteral || !self::isIdentifier($name) ? '{' . self::_dump($name) . '}' - : $name ; + : $name; } diff --git a/src/PhpGenerator/Method.php b/src/PhpGenerator/Method.php index 3823391a..3261bf85 100644 --- a/src/PhpGenerator/Method.php +++ b/src/PhpGenerator/Method.php @@ -1,8 +1,8 @@ Parameter */ - private $parameters = array(); + private $parameters = []; /** @var array of name => bool */ - private $uses = array(); + private $uses = []; /** @var string|FALSE */ - private $body; + private $body = ''; /** @var bool */ private $static = FALSE; @@ -47,86 +49,105 @@ class Method extends Nette\Object /** @var bool */ private $variadic = FALSE; - /** @var array of string */ - private $documents = array(); + /** @var string|NULL */ + private $comment; - /** @var PhpNamespace */ + /** @var PhpNamespace|NULL */ private $namespace; + /** @var string|NULL */ + private $returnType; + /** * @return self */ public static function from($from) { - $from = $from instanceof \ReflectionMethod ? $from : new \ReflectionMethod($from); - $method = new static; - $method->name = $from->getName(); + if (is_string($from) && strpos($from, '::')) { + $from = new \ReflectionMethod($from); + } elseif (is_array($from)) { + $from = new \ReflectionMethod($from[0], $from[1]); + } elseif (!$from instanceof \ReflectionFunctionAbstract) { + $from = new \ReflectionFunction($from); + } + + $method = new static($from->isClosure() ? NULL : $from->getName()); foreach ($from->getParameters() as $param) { $method->parameters[$param->getName()] = Parameter::from($param); } - $method->static = $from->isStatic(); - $method->visibility = $from->isPrivate() ? 'private' : ($from->isProtected() ? 'protected' : ''); - $method->final = $from->isFinal(); - $method->abstract = $from->isAbstract() && !$from->getDeclaringClass()->isInterface(); - $method->body = $from->isAbstract() ? FALSE : ''; + if ($from instanceof \ReflectionMethod) { + $method->static = $from->isStatic(); + $method->visibility = $from->isPrivate() ? 'private' : ($from->isProtected() ? 'protected' : NULL); + $method->final = $from->isFinal(); + $method->abstract = $from->isAbstract() && !$from->getDeclaringClass()->isInterface(); + $method->body = $from->isAbstract() ? FALSE : ''; + } $method->returnReference = $from->returnsReference(); - $method->variadic = PHP_VERSION_ID >= 50600 && $from->isVariadic(); - $method->documents = preg_replace('#^\s*\* ?#m', '', trim($from->getDocComment(), "/* \r\n\t")); + $method->variadic = $from->isVariadic(); + $method->comment = $from->getDocComment() ? preg_replace('#^\s*\* ?#m', '', trim($from->getDocComment(), "/* \r\n\t")) : NULL; + if (PHP_VERSION_ID >= 70000 && $from->hasReturnType()) { + $method->returnType = (string) $from->getReturnType(); + } return $method; } + /** + * @param string|NULL + */ + public function __construct($name = NULL) + { + $this->setName($name); + } + + /** * @return string PHP code */ public function __toString() { - $parameters = array(); + $parameters = []; foreach ($this->parameters as $param) { $variadic = $this->variadic && $param === end($this->parameters); - $hint = in_array($param->getTypeHint(), array('array', '')) - ? $param->getTypeHint() - : ($this->namespace ? $this->namespace->unresolveName($param->getTypeHint()) : $param->getTypeHint()); - - $parameters[] = ($hint ? $hint . ' ' : '') + $hint = $param->getTypeHint(); + $parameters[] = ($hint ? ($this->namespace ? $this->namespace->unresolveName($hint) : $hint) . ' ' : '') . ($param->isReference() ? '&' : '') . ($variadic ? '...' : '') . '$' . $param->getName() . ($param->isOptional() && !$variadic ? ' = ' . Helpers::dump($param->defaultValue) : ''); } - $uses = array(); + $uses = []; foreach ($this->uses as $param) { $uses[] = ($param->isReference() ? '&' : '') . '$' . $param->getName(); } - return ($this->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $this->documents)) . "\n */\n" : '') + + return ($this->comment ? str_replace("\n", "\n * ", "/**\n" . $this->comment) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . ($this->visibility ? $this->visibility . ' ' : '') . ($this->static ? 'static ' : '') . 'function' . ($this->returnReference ? ' &' : '') - . ($this->name ? ' ' . $this->name : '') + . ' ' . $this->name . '(' . implode(', ', $parameters) . ')' . ($this->uses ? ' use (' . implode(', ', $uses) . ')' : '') + . ($this->returnType ? ': ' . ($this->namespace ? $this->namespace->unresolveName($this->returnType) : $this->returnType) : '') . ($this->abstract || $this->body === FALSE ? ';' - : ($this->name ? "\n" : ' ') . "{\n" . Nette\Utils\Strings::indent(trim($this->body), 1) . "\n}"); + : ($this->name ? "\n" : ' ') . "{\n" . Nette\Utils\Strings::indent(ltrim(rtrim($this->body) . "\n"), 1) . '}'); } - /** - * @param string - * @return self - */ + /** @deprecated */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name ? (string) $name : NULL; return $this; } /** - * @return string + * @return string|NULL */ public function getName() { @@ -140,12 +161,13 @@ public function getName() */ public function setParameters(array $val) { + $this->parameters = []; foreach ($val as $v) { if (!$v instanceof Parameter) { throw new Nette\InvalidArgumentException('Argument must be Nette\PhpGenerator\Parameter[].'); } + $this->parameters[$v->getName()] = $v; } - $this->parameters = $val; return $this; } @@ -165,11 +187,11 @@ public function getParameters() */ public function addParameter($name, $defaultValue = NULL) { - $param = new Parameter; + $param = new Parameter($name); if (func_num_args() > 1) { $param->setOptional(TRUE)->setDefaultValue($defaultValue); } - return $this->parameters[$name] = $param->setName($name); + return $this->parameters[$name] = $param; } @@ -197,8 +219,7 @@ public function getUses() */ public function addUse($name) { - $param = new Parameter; - return $this->uses[] = $param->setName($name); + return $this->uses[] = new Parameter($name); } @@ -257,16 +278,16 @@ public function isStatic() */ public function setVisibility($val) { - if (!in_array($val, array('public', 'protected', 'private', NULL), TRUE)) { + if (!in_array($val, ['public', 'protected', 'private', NULL], TRUE)) { throw new Nette\InvalidArgumentException('Argument must be public|protected|private|NULL.'); } - $this->visibility = (string) $val; + $this->visibility = $val ? (string) $val : NULL; return $this; } /** - * @return string + * @return string|NULL */ public function getVisibility() { @@ -355,22 +376,22 @@ public function isVariadic() /** - * @param string[] + * @param string|NULL * @return self */ - public function setDocuments(array $val) + public function setComment($val) { - $this->documents = $val; + $this->comment = $val ? (string) $val : NULL; return $this; } /** - * @return string[] + * @return string|NULL */ - public function getDocuments() + public function getComment() { - return $this->documents; + return $this->comment; } @@ -378,13 +399,37 @@ public function getDocuments() * @param string * @return self */ - public function addDocument($val) + public function addComment($val) { - $this->documents[] = (string) $val; + $this->comment .= $this->comment ? "\n$val" : $val; return $this; } + /** @deprecated */ + public function setDocuments(array $s) + { + trigger_error(__METHOD__ . '() is deprecated, use similar setComment()', E_USER_DEPRECATED); + return $this->setComment(implode("\n", $s)); + } + + + /** @deprecated */ + public function getDocuments() + { + trigger_error(__METHOD__ . '() is deprecated, use similar getComment()', E_USER_DEPRECATED); + return $this->comment ? [$this->comment] : []; + } + + + /** @deprecated */ + public function addDocument($s) + { + trigger_error(__METHOD__ . '() is deprecated, use addComment()', E_USER_DEPRECATED); + return $this->addComment($s); + } + + /** * @return self */ @@ -394,4 +439,24 @@ public function setNamespace(PhpNamespace $val = NULL) return $this; } + + /** + * @param string|NULL + * @return self + */ + public function setReturnType($val) + { + $this->returnType = $val ? (string) $val : NULL; + return $this; + } + + + /** + * @return string|NULL + */ + public function getReturnType() + { + return $this->returnType; + } + } diff --git a/src/PhpGenerator/Parameter.php b/src/PhpGenerator/Parameter.php index a70d72e7..9ed048cc 100644 --- a/src/PhpGenerator/Parameter.php +++ b/src/PhpGenerator/Parameter.php @@ -1,8 +1,8 @@ name = $from->getName(); + $param = new static($from->getName()); $param->reference = $from->isPassedByReference(); - try { - $param->typeHint = $from->isArray() ? 'array' : ($from->getClass() ? '\\' . $from->getClass()->getName() : ''); - } catch (\ReflectionException $e) { - if (preg_match('#Class (.+) does not exist#', $e->getMessage(), $m)) { - $param->typeHint = '\\' . $m[1]; - } else { - throw $e; + if (PHP_VERSION_ID >= 70000) { + $param->typeHint = $from->hasType() ? (string) $from->getType() : NULL; + } elseif ($from->isArray() || $from->isCallable()) { + $param->typeHint = $from->isArray() ? 'array' : 'callable'; + } else { + try { + $param->typeHint = $from->getClass() ? $from->getClass()->getName() : NULL; + } catch (\ReflectionException $e) { + if (preg_match('#Class (.+) does not exist#', $e->getMessage(), $m)) { + $param->typeHint = $m[1]; + } else { + throw $e; + } } } - $param->optional = PHP_VERSION_ID < 50407 ? $from->isOptional() || ($param->typeHint && $from->allowsNull()) : $from->isDefaultValueAvailable(); - $param->defaultValue = (PHP_VERSION_ID === 50316 ? $from->isOptional() : $from->isDefaultValueAvailable()) ? $from->getDefaultValue() : NULL; - - $namespace = $from->getDeclaringClass()->getNamespaceName(); - $namespace = $namespace ? "\\$namespace\\" : "\\"; - if (Nette\Utils\Strings::startsWith($param->typeHint, $namespace)) { - $param->typeHint = substr($param->typeHint, strlen($namespace)); - } + $param->optional = $from->isDefaultValueAvailable(); + $param->defaultValue = $from->isDefaultValueAvailable() ? $from->getDefaultValue() : NULL; return $param; } /** * @param string without $ - * @return self */ + public function __construct($name = '') + { + $this->setName($name); + } + + + /** @deprecated */ public function setName($name) { $this->name = (string) $name; @@ -103,18 +108,18 @@ public function isReference() /** - * @param string + * @param string|NULL * @return self */ public function setTypeHint($hint) { - $this->typeHint = (string) $hint; + $this->typeHint = $hint ? (string) $hint : NULL; return $this; } /** - * @return string + * @return string|NULL */ public function getTypeHint() { diff --git a/src/PhpGenerator/PhpFile.php b/src/PhpGenerator/PhpFile.php index a210eb67..9755ef40 100644 --- a/src/PhpGenerator/PhpFile.php +++ b/src/PhpGenerator/PhpFile.php @@ -1,13 +1,13 @@ */ -class PhpFile extends Object +class PhpFile { - /** @var string[] */ - private $documents; + use Nette\SmartObject; + + /** @var string|NULL */ + private $comment; /** @var PhpNamespace[] */ - private $namespaces = array(); + private $namespaces = []; /** - * @return string[] + * @param string|NULL + * @return self */ - public function getDocuments() + public function setComment($val) { - return $this->documents; + $this->comment = $val ? (string) $val : NULL; + return $this; } /** - * @param string[] - * @return self + * @return string|NULL */ - public function setDocuments(array $documents) + public function getComment() { - $this->documents = $documents; - return $this; + return $this->comment; } @@ -54,13 +54,37 @@ public function setDocuments(array $documents) * @param string * @return self */ - public function addDocument($document) + public function addComment($val) { - $this->documents[] = $document; + $this->comment .= $this->comment ? "\n$val" : $val; return $this; } + /** @deprecated */ + public function setDocuments(array $s) + { + trigger_error(__METHOD__ . '() is deprecated, use similar setComment()', E_USER_DEPRECATED); + return $this->setComment(implode("\n", $s)); + } + + + /** @deprecated */ + public function getDocuments() + { + trigger_error(__METHOD__ . '() is deprecated, use similar getComment()', E_USER_DEPRECATED); + return $this->comment ? [$this->comment] : []; + } + + + /** @deprecated */ + public function addDocument($s) + { + trigger_error(__METHOD__ . '() is deprecated, use addComment()', E_USER_DEPRECATED); + return $this->addComment($s); + } + + /** * @param string * @return ClassType @@ -116,12 +140,12 @@ public function addNamespace($name) public function __toString() { foreach ($this->namespaces as $namespace) { - $namespace->setBracketedSyntax(isset($this->namespaces[NULL])); + $namespace->setBracketedSyntax(count($this->namespaces) > 1 && isset($this->namespaces[NULL])); } return Strings::normalize( "documents ? "\n" . str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $this->documents)) . "\n */\n\n" : '') + . ($this->comment ? "\n" . str_replace("\n", "\n * ", "/**\n" . $this->comment) . "\n */\n\n" : '') . implode("\n\n", $this->namespaces) ) . "\n"; } diff --git a/src/PhpGenerator/PhpLiteral.php b/src/PhpGenerator/PhpLiteral.php index 19f23c92..1bf8d000 100644 --- a/src/PhpGenerator/PhpLiteral.php +++ b/src/PhpGenerator/PhpLiteral.php @@ -1,19 +1,15 @@ */ -class PhpNamespace extends Object +class PhpNamespace { + use Nette\SmartObject; + /** @var string */ private $name; @@ -31,10 +31,10 @@ class PhpNamespace extends Object private $bracketedSyntax = FALSE; /** @var string[] */ - private $uses = array(); + private $uses = []; /** @var ClassType[] */ - private $classes = array(); + private $classes = []; public function __construct($name = NULL) @@ -43,19 +43,7 @@ public function __construct($name = NULL) } - /** - * @return string - */ - public function getName() - { - return $this->name; - } - - - /** - * @param string - * @return self - */ + /** @deprecated */ public function setName($name) { $this->name = (string) $name; @@ -64,11 +52,11 @@ public function setName($name) /** - * @return bool + * @return string|NULL */ - public function getBracketedSyntax() + public function getName() { - return $this->bracketedSyntax; + return $this->name ?: NULL; } @@ -85,11 +73,11 @@ public function setBracketedSyntax($state = TRUE) /** - * @return string[] + * @return bool */ - public function getUses() + public function getBracketedSyntax() { - return $this->uses; + return $this->bracketedSyntax; } @@ -130,12 +118,24 @@ public function addUse($name, $alias = NULL, &$aliasOut = NULL) } + /** + * @return string[] + */ + public function getUses() + { + return $this->uses; + } + + /** * @param string * @return string */ public function unresolveName($name) { + if (in_array(strtolower($name), ['self', 'parent', 'array', 'callable', 'string', 'bool', 'float', 'int', ''], TRUE)) { + return $name; + } $name = ltrim($name, '\\'); $res = NULL; $lower = strtolower($name); @@ -151,20 +151,11 @@ public function unresolveName($name) if (!$res && Strings::startsWith($lower, strtolower($this->name) . '\\')) { return substr($name, strlen($this->name) + 1); } else { - return $res ?: '\\' . $name; + return $res ?: ($this->name ? '\\' : '') . $name; } } - /** - * @return ClassType[] - */ - public function getClasses() - { - return $this->classes; - } - - /** * @param string * @return ClassType @@ -199,12 +190,21 @@ public function addTrait($name) } + /** + * @return ClassType[] + */ + public function getClasses() + { + return $this->classes; + } + + /** * @return string PHP code */ public function __toString() { - $uses = array(); + $uses = []; asort($this->uses); foreach ($this->uses as $alias => $name) { $useNamespace = Helpers::extractNamespace($name); diff --git a/src/PhpGenerator/Property.php b/src/PhpGenerator/Property.php index 558eb6b8..b742f106 100644 --- a/src/PhpGenerator/Property.php +++ b/src/PhpGenerator/Property.php @@ -1,8 +1,8 @@ name = $from->getName(); + $prop = new static($from->getName()); $defaults = $from->getDeclaringClass()->getDefaultProperties(); $prop->value = isset($defaults[$prop->name]) ? $defaults[$prop->name] : NULL; $prop->static = $from->isStatic(); $prop->visibility = $from->isPrivate() ? 'private' : ($from->isProtected() ? 'protected' : 'public'); - $prop->documents = preg_replace('#^\s*\* ?#m', '', trim($from->getDocComment(), "/* \r\n\t")); + $prop->comment = $from->getDocComment() ? preg_replace('#^\s*\* ?#m', '', trim($from->getDocComment(), "/* \r\n\t")) : NULL; return $prop; } /** * @param string without $ - * @return self */ + public function __construct($name = '') + { + $this->setName($name); + } + + + /** @deprecated */ public function setName($name) { $this->name = (string) $name; @@ -114,7 +119,7 @@ public function isStatic() */ public function setVisibility($val) { - if (!in_array($val, array('public', 'protected', 'private'), TRUE)) { + if (!in_array($val, ['public', 'protected', 'private'], TRUE)) { throw new Nette\InvalidArgumentException('Argument must be public|protected|private.'); } $this->visibility = (string) $val; @@ -132,22 +137,22 @@ public function getVisibility() /** - * @param string[] + * @param string|NULL * @return self */ - public function setDocuments(array $s) + public function setComment($val) { - $this->documents = $s; + $this->comment = $val ? (string) $val : NULL; return $this; } /** - * @return string[] + * @return string|NULL */ - public function getDocuments() + public function getComment() { - return $this->documents; + return $this->comment; } @@ -155,10 +160,34 @@ public function getDocuments() * @param string * @return self */ - public function addDocument($s) + public function addComment($val) { - $this->documents[] = (string) $s; + $this->comment .= $this->comment ? "\n$val" : $val; return $this; } + + /** @deprecated */ + public function setDocuments(array $s) + { + trigger_error(__METHOD__ . '() is deprecated, use similar setComment()', E_USER_DEPRECATED); + return $this->setComment(implode("\n", $s)); + } + + + /** @deprecated */ + public function getDocuments() + { + trigger_error(__METHOD__ . '() is deprecated, use similar getComment()', E_USER_DEPRECATED); + return $this->comment ? [$this->comment] : []; + } + + + /** @deprecated */ + public function addDocument($s) + { + trigger_error(__METHOD__ . '() is deprecated, use addComment()', E_USER_DEPRECATED); + return $this->addComment($s); + } + } diff --git a/tests/.coveralls.yml b/tests/.coveralls.yml new file mode 100644 index 00000000..82764a3f --- /dev/null +++ b/tests/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: travis-ci +coverage_clover: coverage.xml +json_path: coverage.json diff --git a/tests/PhpGenerator/ClassType.expect b/tests/PhpGenerator/ClassType.expect index 317f9a4f..6e20fd0e 100644 --- a/tests/PhpGenerator/ClassType.expect +++ b/tests/PhpGenerator/ClassType.expect @@ -6,19 +6,17 @@ */ abstract final class Example extends ParentClass implements IExample, IOne { - use ObjectTrait; const ROLE = 'admin'; const FORCE_ARRAY = Nette\Utils\Json::FORCE_ARRAY; - /** @var resource orignal file handle */ private $handle; public $order = RecursiveIteratorIterator::SELF_FIRST; - public static $sections = array('first' => TRUE); + public static $sections = ['first' => TRUE]; /** diff --git a/tests/PhpGenerator/ClassType.from.expect b/tests/PhpGenerator/ClassType.from.expect index 697c52cb..0d2122a7 100644 --- a/tests/PhpGenerator/ClassType.from.expect +++ b/tests/PhpGenerator/ClassType.from.expect @@ -11,9 +11,6 @@ interface Interface1 interface Interface2 { - - - } abstract class Class1 implements Interface1 @@ -24,7 +21,6 @@ abstract class Class1 implements Interface1 */ function func1() { - } @@ -34,7 +30,6 @@ abstract class Class1 implements Interface1 class Class2 extends Class1 implements Interface2 { - /** * Public * @var int @@ -44,7 +39,7 @@ class Class2 extends Class1 implements Interface2 /** @var int */ protected $protected = 10; - private $private = array(); + private $private = []; public static $static; @@ -53,15 +48,19 @@ class Class2 extends Class1 implements Interface2 * Func3 * @return Class1 */ - private function & func3(array $a%a?%, Class2 $b = NULL, Unknown $c, \Xyz\Unknown $d, $e) + private function & func3(array $a%a?%, Class2 $b = NULL, Unknown $c, \Xyz\Unknown $d, callable $e, $f) { - } final function func2() { - } -} \ No newline at end of file +} + +class Class3 +{ + public $prop1; + +} diff --git a/tests/PhpGenerator/ClassType.from.php7.expect b/tests/PhpGenerator/ClassType.from.php7.expect new file mode 100644 index 00000000..c23a67bd --- /dev/null +++ b/tests/PhpGenerator/ClassType.from.php7.expect @@ -0,0 +1,26 @@ +class anonymous +{ + public $a; + + private $b; + + + function a() + { + } + + + private function b() + { + } + +} + +class anonymous extends Class1 +{ + + function a() + { + } + +} diff --git a/tests/PhpGenerator/ClassType.from.php7.phpt b/tests/PhpGenerator/ClassType.from.php7.phpt new file mode 100644 index 00000000..17f0090c --- /dev/null +++ b/tests/PhpGenerator/ClassType.from.php7.phpt @@ -0,0 +1,32 @@ +prop2 = 1; +$res[] = ClassType::from(new \ReflectionObject($obj)); Assert::matchFile(__DIR__ . '/ClassType.from.expect', implode("\n", $res)); diff --git a/tests/PhpGenerator/ClassType.from.trait.expect b/tests/PhpGenerator/ClassType.from.trait.expect index 19be7237..2aebe46f 100644 --- a/tests/PhpGenerator/ClassType.from.trait.expect +++ b/tests/PhpGenerator/ClassType.from.trait.expect @@ -6,7 +6,6 @@ trait Trait1 function func1() { - } } @@ -16,7 +15,6 @@ trait Trait2 protected function func2() { - } } @@ -26,20 +24,15 @@ abstract class Class1 function func1() { - } protected function func2() { - } } class Class2 extends Class1 { - - - } \ No newline at end of file diff --git a/tests/PhpGenerator/ClassType.from.trait.phpt b/tests/PhpGenerator/ClassType.from.trait.phpt index 1c2f1f75..23fa076c 100644 --- a/tests/PhpGenerator/ClassType.from.trait.phpt +++ b/tests/PhpGenerator/ClassType.from.trait.phpt @@ -2,11 +2,10 @@ /** * Test: Nette\PhpGenerator generator. - * @phpversion 5.4 */ -use Nette\PhpGenerator\ClassType, - Tester\Assert; +use Nette\PhpGenerator\ClassType; +use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -29,7 +28,8 @@ trait Trait2 abstract class Class1 { - use Trait1, Trait2; + use Trait1; + use Trait2; } class Class2 extends Class1 diff --git a/tests/PhpGenerator/ClassType.inheritance.expect b/tests/PhpGenerator/ClassType.inheritance.expect index b35bc9e3..934924d3 100644 --- a/tests/PhpGenerator/ClassType.inheritance.expect +++ b/tests/PhpGenerator/ClassType.inheritance.expect @@ -1,6 +1,5 @@ class B extends A { - public $d; protected $e; @@ -10,7 +9,6 @@ class B extends A function bar() { - } } diff --git a/tests/PhpGenerator/ClassType.inheritance.phpt b/tests/PhpGenerator/ClassType.inheritance.phpt index 93441050..3aad1bfc 100644 --- a/tests/PhpGenerator/ClassType.inheritance.phpt +++ b/tests/PhpGenerator/ClassType.inheritance.phpt @@ -1,8 +1,8 @@ setType('interface') ->addExtend('IOne') ->addExtend('ITwo') - ->addDocument('Description of interface'); + ->addComment('Description of interface'); $interface->addMethod('getForm'); diff --git a/tests/PhpGenerator/ClassType.phpt b/tests/PhpGenerator/ClassType.phpt index 49569e8e..13a7f5ff 100644 --- a/tests/PhpGenerator/ClassType.phpt +++ b/tests/PhpGenerator/ClassType.phpt @@ -4,9 +4,9 @@ * Test: Nette\PhpGenerator for classes. */ -use Nette\PhpGenerator\ClassType, - Nette\PhpGenerator\PhpLiteral, - Tester\Assert; +use Nette\PhpGenerator\ClassType; +use Nette\PhpGenerator\PhpLiteral; +use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -20,8 +20,8 @@ $class ->addImplement('IExample') ->addImplement('IOne') ->addTrait('ObjectTrait') - ->addDocument("Description of class.\nThis is example\n") - ->addDocument('@property-read Nette\Forms\Form $form'); + ->addComment("Description of class.\nThis is example\n") + ->addComment('@property-read Nette\Forms\Form $form'); $class ->addConst('ROLE', 'admin') @@ -29,21 +29,21 @@ $class $class->addProperty('handle') ->setVisibility('private') - ->addDocument('@var resource orignal file handle'); + ->addComment('@var resource orignal file handle'); $class->addProperty('order') ->setValue(new PhpLiteral('RecursiveIteratorIterator::SELF_FIRST')); -$p = $class->addProperty('sections', array('first' => TRUE)) +$p = $class->addProperty('sections', ['first' => TRUE]) ->setStatic(TRUE); Assert::same($p, $class->getProperty('sections')); $m = $class->addMethod('getHandle') - ->addDocument('Returns file handle.') - ->addDocument('@return resource') + ->addComment('Returns file handle.') + ->addComment('@return resource') ->setFinal(TRUE) - ->setBody('return $this->?;', array('handle')); + ->setBody('return $this->?;', ['handle']); Assert::same($m, $class->getMethod('getHandle')); @@ -51,7 +51,7 @@ $class->addMethod('getSections') ->setStatic(TRUE) ->setVisibility('protected') ->setReturnReference(TRUE) - ->addBody('$mode = ?;', array(123)) + ->addBody('$mode = ?;', [123]) ->addBody('return self::$sections;') ->addParameter('mode', new PhpLiteral('self::ORDER')); @@ -65,3 +65,20 @@ $method->addParameter('res', NULL) ->setTypeHint('array'); Assert::matchFile(__DIR__ . '/ClassType.expect', (string) $class); + + +// global setters & getters +$methods = $class->getMethods(); +Assert::count(3, $methods); +$class->setMethods(array_values($methods)); +Assert::same($methods, $class->getMethods()); + +$properties = $class->getProperties(); +Assert::count(3, $properties); +$class->setProperties(array_values($properties)); +Assert::same($properties, $class->getProperties()); + +$parameters = $method->getParameters(); +Assert::count(2, $parameters); +$method->setParameters(array_values($parameters)); +Assert::same($parameters, $method->getParameters()); diff --git a/tests/PhpGenerator/Helpers.dump().php7.phpt b/tests/PhpGenerator/Helpers.dump().php7.phpt new file mode 100644 index 00000000..c06d4cb3 --- /dev/null +++ b/tests/PhpGenerator/Helpers.dump().php7.phpt @@ -0,0 +1,17 @@ + 'b', 'c', '9a' => 'd', 'e')", Helpers::dump(array('a', 7 => 'b', 'c', '9a' => 'd', 9 => 'e')) ); -Assert::same( "array(\n\tarray(\n\t\t'a',\n\t\t'loooooooooooooooooooooooooooooooooong',\n\t),\n)", Helpers::dump(array(array('a', 'loooooooooooooooooooooooooooooooooong'))) ); -Assert::same( "array('a' => 1, array(\"\\r\" => \"\\r\", 2), 3)", Helpers::dump(array('a' => 1, array("\r" => "\r", 2), 3)) ); +Assert::same('[1, 2, 3]', Helpers::dump([1, 2, 3])); +Assert::same("['a', 7 => 'b', 'c', '9a' => 'd', 'e']", Helpers::dump(['a', 7 => 'b', 'c', '9a' => 'd', 9 => 'e'])); +Assert::same("[\n\t[\n\t\t'a',\n\t\t'loooooooooooooooooooooooooooooooooong',\n\t],\n]", Helpers::dump([['a', 'loooooooooooooooooooooooooooooooooong']])); +Assert::same("['a' => 1, [\"\\r\" => \"\\r\", 2], 3]", Helpers::dump(['a' => 1, ["\r" => "\r", 2], 3])); -Assert::same( "(object) array(\n\t'a' => 1,\n\t'b' => 2,\n)", Helpers::dump((object) array('a' => 1, 'b' => 2)) ); -Assert::same( "(object) array(\n\t'a' => (object) array(\n\t\t'b' => 2,\n\t),\n)" , Helpers::dump((object) array('a' => (object) array('b' => 2))) ); +Assert::same("(object) [\n\t'a' => 1,\n\t'b' => 2,\n]", Helpers::dump((object) ['a' => 1, 'b' => 2])); +Assert::same("(object) [\n\t'a' => (object) [\n\t\t'b' => 2,\n\t],\n]", Helpers::dump((object) ['a' => (object) ['b' => 2]])); class Test @@ -44,8 +51,8 @@ class Test private $c = 3; } -Assert::same( "Nette\\PhpGenerator\\Helpers::createObject('Test', array(\n\t'a' => 1,\n\t\"\\x00*\\x00b\" => 2,\n\t\"\\x00Test\\x00c\" => 3,\n))", Helpers::dump(new Test) ); -Assert::equal( new Test, eval('return ' . Helpers::dump(new Test) . ';') ); +Assert::same("Nette\\PhpGenerator\\Helpers::createObject('Test', [\n\t'a' => 1,\n\t\"\\x00*\\x00b\" => 2,\n\t\"\\x00Test\\x00c\" => 3,\n])", Helpers::dump(new Test)); +Assert::equal(new Test, eval('return ' . Helpers::dump(new Test) . ';')); class Test2 extends Test @@ -55,7 +62,7 @@ class Test2 extends Test function __sleep() { - return array('c', 'b', 'a'); + return ['c', 'b', 'a']; } function __wakeup() @@ -63,8 +70,8 @@ class Test2 extends Test } } -Assert::same( "Nette\\PhpGenerator\\Helpers::createObject('Test2', array(\n\t\"\\x00Test2\\x00c\" => 4,\n\t'a' => 1,\n\t\"\\x00*\\x00b\" => 2,\n))", Helpers::dump(new Test2) ); -Assert::equal( new Test2, eval('return ' . Helpers::dump(new Test2) . ';') ); +Assert::same("Nette\\PhpGenerator\\Helpers::createObject('Test2', [\n\t\"\\x00Test2\\x00c\" => 4,\n\t'a' => 1,\n\t\"\\x00*\\x00b\" => 2,\n])", Helpers::dump(new Test2)); +Assert::equal(new Test2, eval('return ' . Helpers::dump(new Test2) . ';')); class Test3 implements Serializable @@ -81,5 +88,9 @@ class Test3 implements Serializable } } -Assert::same( 'unserialize(\'C:5:"Test3":0:{}\')', Helpers::dump(new Test3) ); -Assert::equal( new Test3, eval('return ' . Helpers::dump(new Test3) . ';') ); +Assert::same('unserialize(\'C:5:"Test3":0:{}\')', Helpers::dump(new Test3)); +Assert::equal(new Test3, eval('return ' . Helpers::dump(new Test3) . ';')); + +Assert::exception(function () { + Helpers::dump(function () {}); +}, Nette\InvalidArgumentException::class, 'Cannot dump closure.'); diff --git a/tests/PhpGenerator/Helpers.format.phpt b/tests/PhpGenerator/Helpers.format.phpt index a08a8a2f..0b662e21 100644 --- a/tests/PhpGenerator/Helpers.format.phpt +++ b/tests/PhpGenerator/Helpers.format.phpt @@ -4,40 +4,40 @@ * Test: Nette\PhpGenerator\Helpers::format() & formatArgs() */ -use Nette\PhpGenerator\Helpers, - Tester\Assert; +use Nette\PhpGenerator\Helpers; +use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -Assert::same( 'func', Helpers::format('func') ); -Assert::same( 'func(1)', Helpers::format('func(?)', 1, 2) ); +Assert::same('func', Helpers::format('func')); +Assert::same('func(1)', Helpers::format('func(?)', 1, 2)); -Assert::same( 'func', Helpers::formatArgs('func', array(1, 2)) ); -Assert::same( 'func(1)', Helpers::formatArgs('func(?)', array(1, 2)) ); -Assert::same( "func(array(1, 2))", Helpers::formatArgs('func(?)', array(array(1, 2))) ); -Assert::same( 'func(1, 2)', Helpers::formatArgs('func(?*)', array(array(1, 2))) ); +Assert::same('func', Helpers::formatArgs('func', [1, 2])); +Assert::same('func(1)', Helpers::formatArgs('func(?)', [1, 2])); +Assert::same('func([1, 2])', Helpers::formatArgs('func(?)', [[1, 2]])); +Assert::same('func(1, 2)', Helpers::formatArgs('func(?*)', [[1, 2]])); Assert::same( "func(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,\n\t35, 36, 37, 38, 39, 40)", - Helpers::formatArgs('func(?*)', array(range(10, 40))) + Helpers::formatArgs('func(?*)', [range(10, 40)]) ); -Assert::exception(function() { - Helpers::formatArgs('func(?*)', array(1, 2)); -}, 'Nette\InvalidArgumentException', 'Argument must be an array.'); +Assert::exception(function () { + Helpers::formatArgs('func(?*)', [1, 2]); +}, Nette\InvalidArgumentException::class, 'Argument must be an array.'); -Assert::exception(function() { - Helpers::formatArgs('func(?, ?, ?)', array(1, 2)); -}, 'Nette\InvalidArgumentException', 'Insufficient number of arguments.'); +Assert::exception(function () { + Helpers::formatArgs('func(?, ?, ?)', [1, 2]); +}, Nette\InvalidArgumentException::class, 'Insufficient number of arguments.'); -Assert::same( '$a = 2', Helpers::formatArgs('$? = ?', array('a', 2)) ); -Assert::same( '$obj->a = 2', Helpers::formatArgs('$obj->? = ?', array('a', 2)) ); -Assert::same( '$obj->{1} = 2', Helpers::formatArgs('$obj->? = ?', array(1, 2)) ); -Assert::same( '$obj->{\' \'} = 2', Helpers::formatArgs('$obj->? = ?', array(' ', 2)) ); +Assert::same('$a = 2', Helpers::formatArgs('$? = ?', ['a', 2])); +Assert::same('$obj->a = 2', Helpers::formatArgs('$obj->? = ?', ['a', 2])); +Assert::same('$obj->{1} = 2', Helpers::formatArgs('$obj->? = ?', [1, 2])); +Assert::same('$obj->{\' \'} = 2', Helpers::formatArgs('$obj->? = ?', [' ', 2])); -Assert::same( "Item", Helpers::formatMember('Item') ); -Assert::same( "{'0Item'}", Helpers::formatMember('0Item') ); +Assert::same('Item', Helpers::formatMember('Item')); +Assert::same("{'0Item'}", Helpers::formatMember('0Item')); -Assert::true( Helpers::isIdentifier('Item') ); -Assert::false( Helpers::isIdentifier('0Item') ); +Assert::true(Helpers::isIdentifier('Item')); +Assert::false(Helpers::isIdentifier('0Item')); diff --git a/tests/PhpGenerator/Method.closure.expect b/tests/PhpGenerator/Method.closure.expect deleted file mode 100644 index 65be62f5..00000000 --- a/tests/PhpGenerator/Method.closure.expect +++ /dev/null @@ -1,3 +0,0 @@ -function &($a, $b) use ($this, &$vars) { - return $a + $b; -} \ No newline at end of file diff --git a/tests/PhpGenerator/Method.closure.phpt b/tests/PhpGenerator/Method.closure.phpt deleted file mode 100644 index 35310426..00000000 --- a/tests/PhpGenerator/Method.closure.phpt +++ /dev/null @@ -1,25 +0,0 @@ -setReturnReference(TRUE) - ->setBody('return $a + $b;'); - -$function->addParameter('a'); -$function->addParameter('b'); -$function->addUse('this'); -$function->addUse('vars') - ->setReference(TRUE); - -Assert::matchFile(__DIR__ . '/Method.closure.expect', (string) $function); diff --git a/tests/PhpGenerator/Method.function.phpt b/tests/PhpGenerator/Method.function.phpt new file mode 100644 index 00000000..622764c8 --- /dev/null +++ b/tests/PhpGenerator/Method.function.phpt @@ -0,0 +1,53 @@ +setReturnReference(TRUE) + ->setBody('return $a + $b;'); + +$function->addParameter('a'); +$function->addParameter('b'); +$function->addUse('this'); +$function->addUse('vars') + ->setReference(TRUE); + +Assert::match( +'function & ($a, $b) use ($this, &$vars) { + return $a + $b; +}', (string) $function); + + +/** closure */ +$closure = function (stdClass $a, $b = NULL) {}; +$function = Method::from($closure); +Assert::match( +'/** + * closure + */ +function (stdClass $a, $b = NULL) { +}', (string) $function); + + +/** global */ +function func(stdClass $a, $b = NULL) { +}; + +$function = Method::from('func'); +Assert::match( +'/** + * global + */ +function func(stdClass $a, $b = NULL) +{ +}', (string) $function); diff --git a/tests/PhpGenerator/Method.returnTypes.phpt b/tests/PhpGenerator/Method.returnTypes.phpt new file mode 100644 index 00000000..0b31194f --- /dev/null +++ b/tests/PhpGenerator/Method.returnTypes.phpt @@ -0,0 +1,48 @@ +getReturnType()); + + $method = Method::from(A::class .'::testScalar'); + Assert::same('string', $method->getReturnType()); + + // generating methods with return type declarations + + $method = (new Method('create')) + ->setReturnType('Foo') + ->setBody('return new Foo();'); + + Assert::match( + 'function create(): Foo +{ + return new Foo(); +} +', (string) $method); + +} diff --git a/tests/PhpGenerator/Method.scalarParameters.phpt b/tests/PhpGenerator/Method.scalarParameters.phpt new file mode 100644 index 00000000..8093c458 --- /dev/null +++ b/tests/PhpGenerator/Method.scalarParameters.phpt @@ -0,0 +1,48 @@ +getParameters()['a']->getTypeHint()); + +$method = Method::from(Foo::class . '::scalars'); +Assert::same('bool', $method->getParameters()['b']->getTypeHint()); + +$method = Method::from(Foo::class . '::scalars'); +Assert::same('int', $method->getParameters()['c']->getTypeHint()); + +$method = Method::from(Foo::class . '::scalars'); +Assert::same('float', $method->getParameters()['d']->getTypeHint()); + + +// generating methods with scalar type hints + +$method = (new Method('create')) + ->setBody('return null;'); +$method->addParameter('a')->setTypeHint('string'); +$method->addParameter('b')->setTypeHint('bool'); + +Assert::match( +'function create(string $a, bool $b) +{ + return null; +} +', (string) $method); diff --git a/tests/PhpGenerator/Method.variadics.phpt b/tests/PhpGenerator/Method.variadics.phpt index 147e81a4..21dcbb15 100644 --- a/tests/PhpGenerator/Method.variadics.phpt +++ b/tests/PhpGenerator/Method.variadics.phpt @@ -5,9 +5,9 @@ * @phpversion 5.6 */ -use Nette\PhpGenerator\Method, - Nette\PhpGenerator\Parameter, - Tester\Assert; +use Nette\PhpGenerator\Method; +use Nette\PhpGenerator\Parameter; +use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -34,8 +34,7 @@ Assert::same('array', $method->getParameters()['bar']->getTypeHint()); // test generating // parameterless variadic method -$method = (new Method) - ->setName('variadic') +$method = (new Method('variadic')) ->setVariadic(TRUE) ->setBody('return 42;'); @@ -48,8 +47,7 @@ Assert::match( // variadic method with one parameter -$method = (new Method) - ->setName('variadic') +$method = (new Method('variadic')) ->setVariadic(TRUE) ->setBody('return 42;'); $method->addParameter('foo'); @@ -63,8 +61,7 @@ Assert::match( // variadic method with multiple parameters -$method = (new Method) - ->setName('variadic') +$method = (new Method('variadic')) ->setVariadic(TRUE) ->setBody('return 42;'); $method->addParameter('foo'); @@ -80,8 +77,7 @@ Assert::match( // method with typehinted variadic param -$method = (new Method) - ->setName('variadic') +$method = (new Method('variadic')) ->setVariadic(TRUE) ->setBody('return 42;'); $method->addParameter('foo')->setTypeHint('array'); @@ -95,8 +91,7 @@ Assert::match( // method with typrhinted by-value variadic param -$method = (new Method) - ->setName('variadic') +$method = (new Method('variadic')) ->setVariadic(TRUE) ->setBody('return 42;'); $method->addParameter('foo')->setTypeHint('array')->setReference(TRUE); diff --git a/tests/PhpGenerator/PhpFile.bracketed.expect b/tests/PhpGenerator/PhpFile.bracketed.expect index 671e07b7..02eb1b30 100644 --- a/tests/PhpGenerator/PhpFile.bracketed.expect +++ b/tests/PhpGenerator/PhpFile.bracketed.expect @@ -9,25 +9,17 @@ namespace Foo { class A implements A, \Bar\C { - - use C, \Bar\D; - - + use C; + use \Bar\D; } interface B { - - - } trait C { - - - } } @@ -37,25 +29,16 @@ namespace Bar { class B extends \Foo\A implements \Foo\B { - use \Foo\C; - - } interface C { - - - } trait D { - - - } } @@ -65,23 +48,14 @@ namespace Baz { class E { - - - } interface F extends \Foo\B, \Bar\C { - - - } trait G { - - - } } @@ -91,9 +65,6 @@ namespace { class H { - - - } } @@ -103,9 +74,6 @@ namespace FooBar { class I { - - - } } diff --git a/tests/PhpGenerator/PhpFile.globalNamespace.expect b/tests/PhpGenerator/PhpFile.globalNamespace.expect new file mode 100644 index 00000000..4c39eada --- /dev/null +++ b/tests/PhpGenerator/PhpFile.globalNamespace.expect @@ -0,0 +1,4 @@ +addDocument('This file is auto-generated. DO NOT EDIT!'); -$file->addDocument('Hey there, I\'m here to document things.'); +$file->addComment('This file is auto-generated. DO NOT EDIT!'); +$file->addComment('Hey there, I\'m here to document things.'); $namespaceFoo = $file->addNamespace('Foo'); @@ -73,3 +73,8 @@ $file->addClass('H'); $file->addClass('FooBar\\I'); Assert::matchFile(__DIR__ . '/PhpFile.bracketed.expect', (string) $file); + +$file = new PhpFile; +$file->addClass('A'); + +Assert::matchFile(__DIR__ . '/PhpFile.globalNamespace.expect', (string) $file); diff --git a/tests/PhpGenerator/PhpFile.regular.expect b/tests/PhpGenerator/PhpFile.regular.expect index 614b269d..f8e75ed9 100644 --- a/tests/PhpGenerator/PhpFile.regular.expect +++ b/tests/PhpGenerator/PhpFile.regular.expect @@ -9,25 +9,17 @@ namespace Foo; class A implements A, \Bar\C { - - use C, \Bar\D; - - + use C; + use \Bar\D; } interface B { - - - } trait C { - - - } @@ -35,25 +27,16 @@ namespace Bar; class B extends \Foo\A implements \Foo\B { - use \Foo\C; - - } interface C { - - - } trait D { - - - } @@ -61,21 +44,12 @@ namespace Baz; class E { - - - } interface F extends \Foo\B, \Bar\C { - - - } trait G { - - - } diff --git a/tests/PhpGenerator/PhpNamespace.expect b/tests/PhpGenerator/PhpNamespace.expect index 8ae51347..742da2ba 100644 --- a/tests/PhpGenerator/PhpNamespace.expect +++ b/tests/PhpGenerator/PhpNamespace.expect @@ -4,19 +4,15 @@ use Bar\C; class A implements A, C { - use \Bar\D; - public function test(C $test) - { + public function test(C $a, self $b, parent $c, array $d, callable $e) + { } } interface B { - - - } diff --git a/tests/PhpGenerator/PhpNamespace.fqn.phpt b/tests/PhpGenerator/PhpNamespace.fqn.phpt new file mode 100644 index 00000000..50954e42 --- /dev/null +++ b/tests/PhpGenerator/PhpNamespace.fqn.phpt @@ -0,0 +1,59 @@ +setExtends('\ParentClass') + ->addImplement('One') + ->addImplement('\Two') + ->addTrait('Three') + ->addTrait('\Four'); + +$class->addMethod('one') + ->setReturnType('One'); + +$method = $class->addMethod('two') + ->setReturnType('\Two'); + +$method->addParameter('one') + ->setTypeHint('One'); + +$method->addParameter('two') + ->setTypeHint('\Two'); + +Assert::matchFile(__DIR__ . '/PhpNamespace.fqn1.expect', (string) $class); + + +// global namespace +$class = new ClassType('Example', new PhpNamespace); +$class + ->setExtends('\ParentClass') + ->addImplement('One') + ->addImplement('\Two') + ->addTrait('Three') + ->addTrait('\Four'); + +$class->addMethod('one') + ->setReturnType('One'); + +$method = $class->addMethod('two') + ->setReturnType('\Two'); + +$method->addParameter('one') + ->setTypeHint('One'); + +$method->addParameter('two') + ->setTypeHint('\Two'); + +Assert::matchFile(__DIR__ . '/PhpNamespace.fqn2.expect', (string) $class); diff --git a/tests/PhpGenerator/PhpNamespace.fqn1.expect b/tests/PhpGenerator/PhpNamespace.fqn1.expect new file mode 100644 index 00000000..d36d3e10 --- /dev/null +++ b/tests/PhpGenerator/PhpNamespace.fqn1.expect @@ -0,0 +1,16 @@ +class Example extends \ParentClass implements One, \Two +{ + use Three; + use \Four; + + + public function one(): One + { + } + + + public function two(One $one, \Two $two): \Two + { + } + +} diff --git a/tests/PhpGenerator/PhpNamespace.fqn2.expect b/tests/PhpGenerator/PhpNamespace.fqn2.expect new file mode 100644 index 00000000..617f1078 --- /dev/null +++ b/tests/PhpGenerator/PhpNamespace.fqn2.expect @@ -0,0 +1,16 @@ +class Example extends ParentClass implements One, Two +{ + use Three; + use Four; + + + public function one(): One + { + } + + + public function two(One $one, Two $two): Two + { + } + +} diff --git a/tests/PhpGenerator/PhpNamespace.phpt b/tests/PhpGenerator/PhpNamespace.phpt index 51c36eb0..5a1b5daa 100644 --- a/tests/PhpGenerator/PhpNamespace.phpt +++ b/tests/PhpGenerator/PhpNamespace.phpt @@ -10,17 +10,39 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; +$namespace = new PhpNamespace; + +Assert::same('A', $namespace->unresolveName('A')); +Assert::same('foo\A', $namespace->unresolveName('foo\A')); + +$namespace->addUse('Bar\C'); + +Assert::same('Bar', $namespace->unresolveName('Bar')); +Assert::same('C', $namespace->unresolveName('bar\C')); +Assert::same('C\D', $namespace->unresolveName('Bar\C\D')); + +foreach (['String', 'string', 'int', 'float', 'bool', 'array', 'callable', 'self', 'parent', ''] as $type) { + Assert::same($type, $namespace->unresolveName($type)); +} + + $namespace = new PhpNamespace('Foo'); +Assert::same('\A', $namespace->unresolveName('\A')); Assert::same('\A', $namespace->unresolveName('A')); Assert::same('A', $namespace->unresolveName('foo\A')); $namespace->addUse('Bar\C'); Assert::same('\Bar', $namespace->unresolveName('Bar')); +Assert::same('C', $namespace->unresolveName('\bar\C')); Assert::same('C', $namespace->unresolveName('bar\C')); Assert::same('C\D', $namespace->unresolveName('Bar\C\D')); +foreach (['String', 'string', 'int', 'float', 'bool', 'array', 'callable', 'self', 'parent', ''] as $type) { + Assert::same($type, $namespace->unresolveName($type)); +} + $classA = $namespace->addClass('A'); Assert::same($namespace, $classA->getNamespace()); @@ -28,18 +50,21 @@ Assert::same($namespace, $classA->getNamespace()); $interfaceB = $namespace->addInterface('B'); Assert::same($namespace, $interfaceB->getNamespace()); -Assert::exception(function() use ($namespace) { +Assert::exception(function () use ($namespace) { $traitC = $namespace->addTrait('C'); Assert::same($namespace, $traitC->getNamespace()); -}, 'Nette\InvalidStateException', "Alias 'C' used already for 'Bar\\C', cannot use for 'Foo\\C'."); +}, Nette\InvalidStateException::class, "Alias 'C' used already for 'Bar\\C', cannot use for 'Foo\\C'."); $classA ->addImplement('Foo\\A') ->addImplement('Bar\\C') - ->addTrait('Bar\\D') - ->addMethod('test') - ->addParameter('test') - ->setTypeHint('Bar\C'); + ->addTrait('Bar\\D'); +$method = $classA->addMethod('test'); +$method->addParameter('a')->setTypeHint('Bar\C'); +$method->addParameter('b')->setTypeHint('self'); +$method->addParameter('c')->setTypeHint('parent'); +$method->addParameter('d')->setTypeHint('array'); +$method->addParameter('e')->setTypeHint('callable'); Assert::matchFile(__DIR__ . '/PhpNamespace.expect', (string) $namespace);