Skip to content
This repository was archived by the owner on Dec 29, 2020. It is now read-only.

Commit 77cfd1d

Browse files
committed
Inheritance support
1 parent 20c1402 commit 77cfd1d

File tree

5 files changed

+131
-17
lines changed

5 files changed

+131
-17
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
/vendor/
1+
/vendor/

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# PHPDoc to Type Hint
22

3-
Add automatically scalar type hint and return types to all functions and methods of a PHP project using existing
3+
[![Build Status](https://travis-ci.org/dunglas/phpdoc-to-typehint.svg?branch=master)](https://travis-ci.org/dunglas/phpdoc-to-typehint)
4+
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/dunglas/phpdoc-to-typehint/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/dunglas/phpdoc-to-typehint/?branch=master)
5+
6+
Add automatically scalar type hints and return types to all functions and methods of a PHP project using existing
47
PHPDoc annotations.
58

69
**Warning**: this project is an early stage and is experimental. It **can** damage your code.

src/Converter.php

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313

1414
use phpDocumentor\Reflection\DocBlock;
1515
use phpDocumentor\Reflection\DocBlock\Tag;
16+
use phpDocumentor\Reflection\Fqsen;
17+
use phpDocumentor\Reflection\Php\Class_;
1618
use phpDocumentor\Reflection\Php\File;
19+
use phpDocumentor\Reflection\Php\Interface_;
1720
use phpDocumentor\Reflection\Php\Project;
1821
use phpDocumentor\Reflection\Types\Compound;
1922
use phpDocumentor\Reflection\Types\Null_;
@@ -252,17 +255,102 @@ private function getDocBlock(Project $project, int $objectType, string $namespac
252255
}
253256

254257
$fqsen = $namespace.'\\'.$object;
255-
$function = sprintf('%s::%s()', $fqsen, $function);
258+
$fqfunction = sprintf('%s::%s()', $fqsen, $function);
256259

257260
foreach ($project->getFiles() as $file) {
258261
foreach ($file->$method() as $obj) {
259262
if ($obj->getFqsen()->__toString() === $fqsen) {
260-
return $this->getDocBlockForFunction($obj->getMethods(), $function);
263+
$docBlock = $this->getDocBlockForFunction($obj->getMethods(), $fqfunction);
264+
265+
if (
266+
self::OBJECT_TRAIT === $objectType ||
267+
(null !== $docBlock && 0 !== strcasecmp('{@inheritdoc}', trim($docBlock->getSummary())))
268+
) {
269+
return $docBlock;
270+
}
271+
272+
if ($obj instanceof Class_) {
273+
if ($docBlock = $this->getDocBlockForInterfaces($project, $obj->getInterfaces(), $function)) {
274+
return $docBlock;
275+
}
276+
277+
$parentFqsen = $obj->getParent();
278+
if (!$parent = $this->getObject($project, $parentFqsen, self::OBJECT_CLASS)) {
279+
return;
280+
}
281+
282+
return $this->getDocBlock($project, self::OBJECT_CLASS, $this->getNamespace($parentFqsen), $parent->getName(), $function);
283+
}
284+
285+
if ($obj instanceof Interface_ && $docBlock = $this->getDocBlockForInterfaces($project, $obj->getParents(), $function)) {
286+
return $docBlock;
287+
}
261288
}
262289
}
263290
}
264291
}
265292

293+
/**
294+
* Extracts a namespace from a FQSEN.
295+
*
296+
* @param Fqsen $fqsen
297+
*
298+
* @return string
299+
*/
300+
private function getNamespace(Fqsen $fqsen): string
301+
{
302+
$value = $fqsen->__toString();
303+
return substr($value, 0, strrpos($value, '\\'));
304+
}
305+
306+
/**
307+
* Finds a Class_ or an Interface_ instance using its FQSEN.
308+
*
309+
* @param Project $project
310+
* @param Fqsen $fqsen
311+
* @param int $objectType
312+
*
313+
* @return Class_|Interface_|null
314+
*/
315+
private function getObject(Project $project, Fqsen $fqsen, int $objectType)
316+
{
317+
$method = self::OBJECT_CLASS === $objectType ? 'getClasses' : 'getInterfaces';
318+
319+
foreach ($project->getFiles() as $file) {
320+
foreach ($file->$method() as $object) {
321+
if ($object->getFqsen()->__toString() === $fqsen->__toString()) {
322+
return $object;
323+
}
324+
}
325+
}
326+
}
327+
328+
/**
329+
* Gets a DocBlock from an array of interfaces FQSEN instances.
330+
*
331+
* @param Project $project
332+
* @param Fqsen[] $fqsens
333+
* @param string $function
334+
*
335+
* @return DocBlock|null
336+
*/
337+
private function getDocBlockForInterfaces(Project $project, array $fqsens, string $function)
338+
{
339+
foreach ($fqsens as $fqsen) {
340+
$object = $this->getObject($project, $fqsen, self::OBJECT_INTERFACE);
341+
342+
if (!$object) {
343+
continue;
344+
}
345+
346+
$docBlock = $this->getDocBlock($project, self::OBJECT_INTERFACE, $this->getNamespace($fqsen), $object->getName(), $function);
347+
348+
if ($docBlock) {
349+
return $docBlock;
350+
}
351+
}
352+
}
353+
266354
/**
267355
* Gets the DocBlock of a function.
268356
*
@@ -278,17 +366,7 @@ private function getDocBlockForFunction(array $functions, string $function)
278366
continue;
279367
}
280368

281-
// TODO: handle inheritance
282-
283-
if (!$docBlock = $reflectionFunction->getDocblock()) {
284-
return;
285-
}
286-
287-
// TODO: handle {@inheritdoc}
288-
289-
// TODO: cache this for performance
290-
291-
return $docBlock;
369+
return $reflectionFunction->getDocblock();
292370
}
293371
}
294372

tests/Fixtures/BazTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace bar;
44

5-
class BazTrait
5+
trait BazTrait
66
{
77
/**
88
* @param int $a

tests/test.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ public function baz(array $a, int $b): float;
155155
156156
namespace bar;
157157
158-
class BazTrait
158+
trait BazTrait
159159
{
160160
/**
161161
* @param int $a
@@ -171,4 +171,37 @@ protected function inTrait(int $a): \DateTime
171171
, $converter->convert($project, $file));
172172
}
173173

174+
$projectFactory = ProjectFactory::createInstance();
175+
$childPath = __DIR__.'/Fixtures/Child.php';
176+
$project = $projectFactory->create('inheritance', [$childPath, __DIR__.'/Fixtures/Foo.php', __DIR__.'/Fixtures/BarInterface.php']);
177+
178+
foreach ($project->getFiles() as $path => $file) {
179+
if ($childPath === $path) {
180+
same(<<<'PHP'
181+
<?php
182+
183+
namespace bar;
184+
185+
class Child extends Foo implements BarInterface
186+
{
187+
use BazTrait;
188+
189+
public function test(float $a)
190+
{
191+
parent::test($a);
192+
}
193+
194+
/**
195+
* {@inheritdoc}
196+
*/
197+
public function baz(array $a, int $b): float
198+
{
199+
}
200+
}
201+
202+
PHP
203+
, $converter->convert($project, $file));
204+
}
205+
}
206+
174207
echo 'Good job! Everything is fine.'.PHP_EOL;

0 commit comments

Comments
 (0)