Completed
Pull Request — master (#35)
by Saif Eddin
02:55 queued 38s
created

ClosurePrinter::N2L()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.1755

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 14
cts 18
cp 0.7778
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 16
nc 6
nop 1
crap 4.1755
1
<?php
2
3
namespace PhpToZephir\Converter\Printer\Expr;
4
5
use PhpToZephir\Converter\Dispatcher;
6
use PhpToZephir\Logger;
7
use PhpParser\Node;
8
use PhpParser\Node\Expr;
9
use PhpToZephir\NodeFetcher;
10
11
class ClosurePrinter
12
{
13
    /**
14
     * @var Dispatcher
15
     */
16
    private $dispatcher = null;
17
    /**
18
     * @var Logger
19
     */
20
    private $logger = null;
21
22
    private static $converted = array();
23
24
    /**
25
     * @param Dispatcher $dispatcher
26
     * @param Logger     $logger
27
     */
28 81
    public function __construct(Dispatcher $dispatcher, Logger $logger)
29
    {
30 81
        $this->dispatcher = $dispatcher;
31 81
        $this->logger = $logger;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
32 81
    }
33
34
    /**
35
     * @return string
36
     */
37 1
    public static function getType()
38
    {
39 1
        return 'pExpr_Closure';
40
    }
41
42
    /**
43
     * @param Expr\Closure $node
44
     *
45
     * @return string
46
     */
47 1
    public function convert(Expr\Closure $node)
48
    {
49 1
        $methodName = $this->dispatcher->getMetadata()->getClass().$this->dispatcher->getLastMethod();
50 1
        if (isset(self::$converted[$methodName])) {
51
            ++self::$converted[$methodName];
52
        } else {
53 1
            self::$converted[$methodName] = 1;
54
        }
55
56 1
        $name = $methodName.'Closure'.$this->N2L(count(self::$converted[$methodName]));
57
58 1
        $this->logger->logNode(
59 1
            sprintf('Closure does not exist in Zephir, class "%s" with __invoke is created', $name),
60 1
            $node,
61 1
            $this->dispatcher->getMetadata()->getFullQualifiedNameClass()
62 1
        );
63
64 1
        return 'new '.$name.'('.$this->dispatcher->pCommaSeparated($node->uses).')';
65
    }
66
67
    /**
68
     * @param null|string $lastMethod
69
     * @param int         $number
70
     */
71 1
    public function createClosureClass(Expr\Closure $node, $lastMethod, $number)
72
    {
73 1
        $this->logger->trace(__METHOD__.' '.__LINE__, $node, $this->dispatcher->getMetadata()->getFullQualifiedNameClass());
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 124 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
74
75 1
        $name = $this->dispatcher->getMetadata()->getClass().$lastMethod.'Closure'.$this->N2L($number);
76
77 1
        $this->logger->logNode(
78 1
            sprintf('Closure does not exist in Zephir, class "%s" with __invoke is created', $name),
79 1
            $node,
80 1
            $this->dispatcher->getMetadata()->getFullQualifiedNameClass()
81 1
        );
82
83
        return array(
84 1
         'name' => $name,
85 1
         'code' => $this->createClass($name, $this->dispatcher->getMetadata()->getNamespace(), $node),
86 1
        );
87
    }
88
89
    /**
90
     * @param string $name
91
     * @param string $namespace
92
     */
93 1
    private function createClass($name, $namespace, Expr\Closure $node)
94
    {
95
        $class = "namespace $namespace;
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $namespace instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $name instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
96
97 1
class $name
98
{
99 1
";
100
101 1
        foreach ($node->uses as $use) {
102 1
            $class .= '    private '.$use->var.";\n";
103 1
        }
104
105
        $class .= '
106 1
    public function __construct('.(!empty($node->uses) ? ''.$this->dispatcher->pCommaSeparated($node->uses) : '').')
107
    {
108 1
        ';
109 1
        foreach ($node->uses as $use) {
110 1
            $class .= '        let this->'.$use->var.' = '.$use->var.";\n";
111 1
        }
112
        $class .= '
113
    }
114
115 1
    public function __invoke('.$this->dispatcher->pCommaSeparated($node->params).')
116 1
    {'.$this->dispatcher->pStmts($this->convertUseToMemberAttribute($node->stmts, $node->uses)).'
117
    }
118
}
119 1
    ';
120
121 1
        return $class;
122
    }
123
124
    /**
125
     * @param Node[]            $node
126
     * @param Expr\ClosureUse[] $uses
127
     */
128 1
    private function convertUseToMemberAttribute($node, $uses)
129
    {
130 1
        $noFetcher = new NodeFetcher();
131
        
132 1
        foreach ($noFetcher->foreachNodes($node) as &$stmt) {
0 ignored issues
show
Bug introduced by
The expression $noFetcher->foreachNodes($node) cannot be used as a reference.

Let?s assume that you have the following foreach statement:

foreach ($array as &$itemValue) { }

$itemValue is assigned by reference. This is possible because the expression (in the example $array) can be used as a reference target.

However, if we were to replace $array with something different like the result of a function call as in

foreach (getArray() as &$itemValue) { }

then assigning by reference is not possible anymore as there is no target that could be modified.

Available Fixes

1. Do not assign by reference
foreach (getArray() as $itemValue) { }
2. Assign to a local variable first
$array = getArray();
foreach ($array as &$itemValue) {}
3. Return a reference
function &getArray() { $array = array(); return $array; }

foreach (getArray() as &$itemValue) { }
Loading history...
133 1
            if ($stmt['node'] instanceof Expr\Variable) {
134 1
                foreach ($uses as $use) {
135 1
                    if ($use->var === $stmt['node']->name) {
136 1
                        $stmt['node']->name = 'this->'.$stmt['node']->name;
137 1
                    }
138 1
                }
139 1
            }
140 1
        }
141
142 1
        return $node;
143
    }
144
145
    /**
146
     * @param int $number
147
     */
148 1
    private function N2L($number)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
149
    {
150 1
        $result = array();
151 1
        $tens = floor($number / 10);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
152 1
        $units = $number % 10;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
153
154
        $words = array(
155 1
            'units' => array('', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eightteen', 'Nineteen'),
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 216 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
156 1
            'tens' => array('', '', 'Twenty', 'Thirty', 'Fourty', 'Fifty', 'Sixty', 'Seventy', 'Eigthy', 'Ninety'),
157 1
        );
158
159 1
        if ($tens < 2) {
160 1
            $result[] = $words['units'][$tens * 10 + $units];
161 1
        } else {
162
            $result[] = $words['tens'][$tens];
163
164
            if ($units > 0) {
165
                $result[count($result) - 1] .= '-'.$words['units'][$units];
166
            }
167
        }
168
169 1
        if (empty($result[0])) {
170 1
            $result[0] = 'Zero';
171 1
        }
172
173 1
        return trim(implode(' ', $result));
174
    }
175
}
176