Completed
Push — master ( d40050...fc7509 )
by Richard
07:27
created

MethodDocGenerator::generateMethodDocs()   C

Complexity

Conditions 14
Paths 41

Size

Total Lines 53
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 34
nc 41
nop 4
dl 0
loc 53
rs 6.3132
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 15 and the first side effect is on line 213.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
/**
4
 * Assert
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this package in the file LICENSE.txt.
10
 * If you did not receive a copy of the license and are unable to
11
 * obtain it through the world-wide-web, please send an email
12
 * to [email protected] so I can send you a copy immediately.
13
 */
14
15
class MethodDocGenerator
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
16
{
17
    public function generateChainDocs()
18
    {
19
        $phpFile           = __DIR__ . '/../lib/Assert/AssertionChain.php';
20
        $skipParameterTest = function (ReflectionParameter $parameter) {
21
            return $parameter->getPosition() === 0;
22
        };
23
24
        $docs = $this->generateMethodDocs($this->gatherAssertions(), ' * @method AssertionChain %s(%s) %s.', $skipParameterTest);
25
        $this->generateFile($phpFile, $docs, 'class');
26
    }
27
28
    /**
29
     * @param ReflectionMethod[] $methods
30
     * @param string $format
31
     * @param callable|false $skipParameterTest
32
     * @param string $prefix
33
     *
34
     * @return array
35
     */
36
    private function generateMethodDocs(array $methods, $format, $skipParameterTest, $prefix = '')
37
    {
38
        $lines = array();
39
        asort($methods);
40
        foreach ($methods as $method) {
41
            $doc              = $method->getDocComment();
42
            list(, $descriptionLine) = explode("\n", $doc);
43
            $shortDescription = trim(substr($descriptionLine, 7), '.');
44
            $methodName       = $prefix . ($prefix ? ucfirst($method->getName()) : $method->getName());
45
46
            $parameters = array();
47
48
            foreach ($method->getParameters() as $methodParameter) {
49
                if (
50
                    (is_bool($skipParameterTest) && $skipParameterTest) ||
51
                    (is_callable($skipParameterTest) && $skipParameterTest($methodParameter))
52
                ) {
53
                    continue;
54
                }
55
56
                $parameter = '$' . $methodParameter->getName();
57
58
                $type = $methodParameter->getType();
59
                if (is_null($type)) {
60
                    preg_match(sprintf('`\* @param (?P<type>[^ ]++) +\%s`sim', $parameter), $doc, $matches);
61
                    if (isset($matches['type'])) {
62
                        $type = (
63
                            $methodParameter->isOptional() &&
64
                            null == $methodParameter->getDefaultValue()
65
                        )
66
                            ? str_replace('|null', '', $matches['type'])
67
                            : $matches['type'];
68
                    }
69
                }
70
                \Assert\Assertion::notEmpty($type, sprintf('No type defined for %s in %s', $parameter, $methodName));
71
                $parameter = sprintf('%s %s', $type, $parameter);
72
73
                if ($methodParameter->isOptional()) {
74
                    if (null === $methodParameter->getDefaultValue()) {
75
                        $parameter .= ' = null';
76
                    } else {
77
                        $parameter .= sprintf(' = \'%s\'', $methodParameter->getDefaultValue());
78
                    }
79
                }
80
81
                $parameters[] = $parameter;
82
            }
83
84
            $lines[] = sprintf($format, $methodName, implode(', ', $parameters), $shortDescription);
85
        }
86
87
        return $lines;
88
    }
89
90
    /**
91
     * @return ReflectionMethod[]
92
     */
93
    private function gatherAssertions()
94
    {
95
        $reflClass = new ReflectionClass('Assert\Assertion');
96
97
        return array_filter(
98
            $reflClass->getMethods(ReflectionMethod::IS_STATIC),
99
            function (ReflectionMethod $reflMethod) {
100
                if ($reflMethod->isProtected()) {
101
                    return false;
102
                }
103
104
                if (in_array($reflMethod->getName(), array('__callStatic', 'createException', 'stringify'))) {
105
                    return false;
106
                }
107
108
                return true;
109
            }
110
        );
111
    }
112
113
    /**
114
     * @param string $phpFile
115
     * @param string[] $lines
116
     * @param string $fileType
117
     */
118
    private function generateFile($phpFile, $lines, $fileType)
119
    {
120
        $phpFile = realpath($phpFile);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $phpFile. This often makes code more readable.
Loading history...
121
        $fileContent = file_get_contents($phpFile);
122
123
        switch ($fileType) {
124
            case 'class':
125
                $fileContent = preg_replace(
126
                    '`\* @method.*? \*/\nclass `sim',
127
                    sprintf("%s\n */\nclass ", trim(implode("\n", $lines))),
128
                    $fileContent
129
                );
130
                break;
131
            case 'readme':
132
                $fileContent = preg_replace(
133
                    '/```php\n<\?php\nuse Assert\\\Assertion;\n\nAssertion::.*?```/sim',
134
                    sprintf("```php\n<?php\nuse Assert\\Assertion;\n\n%s\n\n```", implode("\n", $lines)),
135
                    $fileContent
136
                );
137
                break;
138
        }
139
140
        $writtenBytes = file_put_contents($phpFile, $fileContent);
141
142
        if ($writtenBytes !== false) {
143
            echo 'Generated ' . $phpFile . '.' . PHP_EOL;
144
        }
145
    }
146
147
    public function generateAssertionDocs()
148
    {
149
        $phpFile           = __DIR__ . '/../lib/Assert/Assertion.php';
150
        $skipParameterTest = function () {
151
            return false;
152
        };
153
154
        $docs = array_merge(
155
            $this->generateMethodDocs($this->gatherAssertions(), ' * @method static bool %s(%s) %s for all values.', $skipParameterTest, 'all'),
156
            $this->generateMethodDocs($this->gatherAssertions(), ' * @method static bool %s(%s) %s or that the value is null.', $skipParameterTest, 'nullOr')
157
        );
158
159
        $this->generateFile($phpFile, $docs, 'class');
160
    }
161
162
    public function generateReadMe()
163
    {
164
        $mdFile            = __DIR__ . '/../README.md';
165
        $skipParameterTest = function (ReflectionParameter $parameter) {
166
            return in_array($parameter->getName(), array('message', 'propertyPath', 'encoding'));
167
        };
168
169
        $docs = $this->generateMethodDocs($this->gatherAssertions(), 'Assertion::%s(%s);', $skipParameterTest);
170
171
        $this->generateFile($mdFile, $docs, 'readme');
172
    }
173
174
    public function generateLazyAssertionDocs()
175
    {
176
        $phpFile           = __DIR__ . '/../lib/Assert/LazyAssertion.php';
177
        $skipParameterTest = function (ReflectionParameter $parameter) {
178
            return $parameter->getPosition() === 0;
179
        };
180
181
        $docs = array_merge(
182
            $this->generateMethodDocs($this->gatherAssertions(), ' * @method LazyAssertion %s(%s) %s.', $skipParameterTest),
183
            $this->generateMethodDocs($this->gatherAssertionChainSwitches(), ' * @method LazyAssertion %s(%s) %s.', false)
0 ignored issues
show
Documentation introduced by
$this->gatherAssertionChainSwitches() is of type array<integer,string>, but the function expects a array<integer,object<ReflectionMethod>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
184
        );
185
186
        $this->generateFile($phpFile, $docs, 'class');
187
    }
188
189
    /**
190
     * @return string[]
191
     */
192
    private function gatherAssertionChainSwitches()
193
    {
194
        $reflClass = new ReflectionClass('Assert\AssertionChain');
195
196
        return array_filter(
197
            $reflClass->getMethods(ReflectionMethod::IS_PUBLIC),
198
            function (ReflectionMethod $reflMethod) {
199
                if (!$reflMethod->isPublic()) {
200
                    return false;
201
                }
202
203
                if (in_array($reflMethod->getName(), array('__construct', '__call', 'setAssertionClassName'))) {
204
                    return false;
205
                }
206
207
                return true;
208
            }
209
        );
210
    }
211
}
212
213
require_once __DIR__ . "/../vendor/autoload.php";
214
215
$generator = new MethodDocGenerator();
216
$generator->generateAssertionDocs();
217
$generator->generateChainDocs();
218
$generator->generateLazyAssertionDocs();
219
$generator->generateReadMe();
220