Completed
Push — master ( b5fe4d...fb5a5d )
by Andrii
13:28
created

Helper::dumpClosure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 1
c 2
b 0
f 0
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * Composer plugin for config assembling
4
 *
5
 * @link      https://github.com/hiqdev/composer-config-plugin
6
 * @package   composer-config-plugin
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2016-2018, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\composer\config;
12
13
use Closure;
14
use Opis\Closure\ReflectionClosure;
15
use ReflectionFunction;
16
use Riimu\Kit\PHPEncoder\PHPEncoder;
17
use Opis\Closure\SerializableClosure;
18
19
/**
20
 * Helper class.
21
 *
22
 * @author Andrii Vasyliev <[email protected]>
23
 */
24
class Helper
25
{
26
    /**
27
     * Merges two or more arrays into one recursively.
28
     * Based on Yii2 yii\helpers\BaseArrayHelper::merge.
29
     * @return array the merged array
30
     */
31
    public static function mergeConfig(): array
32
    {
33
        $args = \func_get_args();
34
        $res = array_shift($args) ?: [];
35
        foreach ($args as $items) {
36
            if (!\is_array($items)) {
37
                continue;
38
            }
39
            foreach ($items as $k => $v) {
40
                if ($v instanceof \yii\helpers\UnsetArrayValue || $v instanceof \Yiisoft\Arrays\UnsetArrayValue) {
0 ignored issues
show
Bug introduced by
The type Yiisoft\Arrays\UnsetArrayValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type yii\helpers\UnsetArrayValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
41
                    unset($res[$k]);
42
                } elseif ($v instanceof \yii\helpers\ReplaceArrayValue || $v instanceof \Yiisoft\Arrays\ReplaceArrayValue) {
0 ignored issues
show
Bug introduced by
The type yii\helpers\ReplaceArrayValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Yiisoft\Arrays\ReplaceArrayValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
43
                    $res[$k] = $v->value;
44
                } elseif (\is_int($k)) {
45
                    /// XXX skip repeated values
46
                    if (\in_array($v, $res, true))  {
47
                        continue;
48
                    }
49
                    if (isset($res[$k])) {
50
                        $res[] = $v;
51
                    } else {
52
                        $res[$k] = $v;
53
                    }
54
                } elseif (\is_array($v) && isset($res[$k]) && \is_array($res[$k])) {
55
                    $res[$k] = self::mergeConfig($res[$k], $v);
56
                } else {
57
                    $res[$k] = $v;
58
                }
59
            }
60
        }
61
62
        return $res;
63
    }
64
65
    public static function exportDefines(array $defines): string
66
    {
67
        $res = '';
68
        foreach ($defines as $key => $value) {
69
            $var = static::exportVar($value);
70
            $res .= "defined('$key') or define('$key', $var);\n";
71
        }
72
73
        return $res;
74
    }
75
76
    /**
77
     * Returns PHP-executable string representation of given value.
78
     * In contrast to var_dump outputs Closures as PHP code.
79
     * @param mixed $value
80
     * @return string
81
     * @throws \ReflectionException
82
     */
83
    public static function exportVar($value): string
84
    {
85
        $closures = self::collectClosures($value);
86
        $res = static::encodeVar($value);
87
        if (!empty($closures)) {
88
            $subs = [];
89
            foreach ($closures as $key => $closure) {
90
                $subs["'" . $key . "'"] = self::dumpClosure($closure);
91
            }
92
            $res = strtr($res, $subs);
93
        }
94
95
        return $res;
96
    }
97
98
    /**
99
     * Riimu/Kit-PHPEncoder based `var_export` alternative.
100
     * @param mixed $value
101
     * @return string
102
     */
103
    public static function encodeVar($value): string
104
    {
105
        return static::getEncoder()->encode($value);
106
    }
107
108
    private static $encoder;
109
110
    private static function getEncoder()
111
    {
112
        if (static::$encoder === null) {
0 ignored issues
show
Bug introduced by
Since $encoder is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $encoder to at least protected.
Loading history...
113
            static::$encoder = new PHPEncoder([
114
                'object.format' => 'serialize',
115
            ]);
116
        }
117
118
        return static::$encoder;
119
    }
120
121
    /**
122
     * Collects closures from given input.
123
     * Substitutes closures with a tag.
124
     * @param mixed $input will be changed
125
     * @return array array of found closures
126
     */
127
    private static function collectClosures(&$input): array
128
    {
129
        static $closureNo = 1;
130
        $closures = [];
131
        if (\is_array($input)) {
132
            foreach ($input as &$value) {
133
                if (\is_array($value) || $value instanceof Closure) {
134
                    $closures = array_merge($closures, self::collectClosures($value));
135
                }
136
            }
137
        } elseif ($input instanceof Closure) {
138
            ++$closureNo;
139
            $key = "--==<<[[((Closure#$closureNo))]]>>==--";
140
            $closures[$key] = $input;
141
            $input = $key;
142
        }
143
144
        return $closures;
145
    }
146
147
    /**
148
     * Dumps closure object to string.
149
     * Based on http://www.metashock.de/2013/05/dump-source-code-of-closure-in-php/.
150
     * @param Closure $closure
151
     * @return string
152
     * @throws \ReflectionException
153
     */
154
    public static function dumpClosure(Closure $closure): string
155
    {
156
        return (new ReflectionClosure($closure))->getCode();
157
    }
158
}
159