Completed
Push — master ( 02ecea...59b1a5 )
by Nikita
05:13
created

Composer::create()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 3 Features 4
Metric Value
c 6
b 3
f 4
dl 0
loc 24
rs 8.6846
cc 4
eloc 13
nc 6
nop 3
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: Nikita Kotenko
5
 * Date: 26.11.2014
6
 * Time: 14:12
7
 */
8
namespace samsonphp\composer;
9
10
11
/**
12
 * Provide creating sorting list of composer packages
13
 * @package samson
14
 */
15
class Composer
16
{
17
    /** @var string Path to current web-application */
18
    private $systemPath;
19
20
    /** @var string  composer lock file name */
21
    private $lockFileName = 'composer.lock';
22
23
    /** @var array List of available vendors */
24
    private $vendorsList = array();
25
26
    /** @var string $ignoreKey */
27
    private $ignoreKey;
28
29
    /** @var string $includeKey */
30
    private $includeKey;
31
32
    /** @var array List of ignored packages */
33
    private $ignorePackages = array();
34
35
    /** @var array  Packages list with require packages*/
36
    private $packagesList = array();
37
38
	private $packagesListExtra = array();
39
40
    /**
41
     *  Add available vendor
42
     * @param $vendor Available vendor
43
     * @return $this
44
     */
45
    public function vendor($vendor)
46
    {
47
        if (!in_array($vendor, $this->vendorsList)) {
48
            $this->vendorsList[] = $vendor.'/';
49
        }
50
        return $this;
51
    }
52
53
54
    /**
55
     * Set name of composer extra parameter to ignore package
56
     * @param $ignoreKey Name
57
     * @return $this
58
     */
59
    public function ignoreKey($ignoreKey)
60
    {
61
        $this->ignoreKey = $ignoreKey;
62
        return $this;
63
    }
64
65
    /**
66
     * Set name of composer extra parameter to include package
67
     * @param $includeKey Name
68
     * @return $this
69
     */
70
    public function includeKey($includeKey)
71
    {
72
        $this->includeKey = $includeKey;
73
        return $this;
74
    }
75
76
    /**
77
     *  Add ignored package
78
     * @param $vendor Ignored package
79
     * @return $this
80
     */
81
    public function ignorePackage($package)
82
    {
83
        if (!in_array($package, $this->ignorePackages)) {
84
            $this->ignorePackages[] = $package;
85
        }
86
        return $this;
87
    }
88
89
    /**
90
     * Create sorted packages list
91
     * @return array Packages list ('package name'=>'rating')
92
     */
93
    public function create(& $packages, $systemPath, $parameters = array())
94
    {
95
	    $class_vars = get_class_vars(get_class($this));
96
97
	    foreach ($class_vars as $name => $value) {
98
		    if (isset($parameters[$name])) {
99
			    $this->$name = $parameters[$name];
100
		    }
101
	    }
102
        // Composer.lock is always in the project root folder
103
        $path = $systemPath.$this->lockFileName;
104
105
        // Create list of relevant packages with there require packages
106
        $this->packagesFill($this->readFile($path));
107
108
        $resultList = $this->sort();
109
110
	    foreach ($resultList as $package => $rating) {
111
            $required = $this->getRequiredList($package);
112
		    $packages[$package] = $this->packagesListExtra[$package];
113
            $packages[$package]['required'] = $required;
114
            $packages[$package]['composerName'] =$package;
115
	    }
116
    }
117
118
    /**
119
     * Provide creating sorting list
120
     * @return array list of sorted packages
121
     */
122
    public function sort()
123
    {
124
        $list = array();
125
        foreach ($this->packagesList as $package => $requiredList) {
126
            if (!sizeof($list)||!isset($list[$package])) {
127
                $list[$package] = 1;
128
            }
129
            foreach ($requiredList as $requiredPackage) {
130
                if (isset($list[$requiredPackage])) {
131
                    $packagePos =  array_search($package, array_keys($list));
132
                    $requiredPackagePos = array_search($requiredPackage, array_keys($list));
133
                    if ($packagePos < $requiredPackagePos) {
134
                        unset($list[$requiredPackage]);
135
                        $list = $this->insertKeyBefore($list, $package, $requiredPackage);
136
                    }
137
                } else {
138
                    $list = $this->insertKeyBefore($list, $package, $requiredPackage);
139
                }
140
141
            }
142
        }
143
        //$this->checkSort($list);
144
145
        return $list;
146
    }
147
148
    /**
149
     *Check result of sorting
150
     * @param $list final list of packages
151
     *
152
     * @return bool result
153
     */
154
    public function checkSort($list)
155
    {
156
        $status = true;
157
        foreach ($this->packagesList as $package => $requiredList) {
158
            foreach ($requiredList as $requiredPackage) {
159
                if (isset($list[$requiredPackage])) {
160
                    $packagePos =  array_search($package, array_keys($list));
161
                    $requiredPackagePos = array_search($requiredPackage, array_keys($list));
162
                    if ($packagePos < $requiredPackagePos) {
163
                        trace('error pos - '.$packagePos.' < '.$requiredPackagePos);
164
                        $status = false;
165
                    }
166
                } else {
167
                    $status = false;
168
                    trace('error not isset!!!!! - '.$requiredPackage);
169
                }
170
            }
171
        }
172
        return $status;
173
    }
174
175
176
177
    /**
178
     * Create list of relevant packages
179
     * @param $packages Composer lock list of packages
180
     * @return array List of relevant packages
181
     */
182
    private function includeList($packages)
183
    {
184
        $includePackages = array();
185
        foreach ($packages as $package) {
186
            if (!$this->isIgnore($package)) {
187
                if ($this->isInclude($package)) {
188
                    $includePackages[] = $package['name'];
189
                }
190
            }
191
        }
192
        return $includePackages;
193
    }
194
195
    /**
196
     * Is package include
197
     * @param $package Composer package
198
     * @return bool - is package include
199
     */
200
    private function isInclude($package)
201
    {
202
        $include = true;
203
        if (sizeof($this->vendorsList)) {
204
            if (!isset($this->includeKey) || !isset($package['extra'][$this->includeKey])) {
205
                $packageName = $package['name'];
206
			    $vendorName = substr($packageName, 0, strpos($packageName,"/")+1);
207
                $include = in_array($vendorName, $this->vendorsList);
208
            }
209
        }
210
        return $include;
211
    }
212
213
    /**
214
     * Is package ignored
215
     * @param $package Composer package
216
     * @return bool - is package ignored
217
     */
218
    private function isIgnore($package)
219
    {
220
        $isIgnore = false;
221
        if (in_array($package['name'], $this->ignorePackages)) {
222
            $isIgnore = true;
223
        }
224
        if (isset($this->ignoreKey)&&(isset($package['extra'][$this->ignoreKey]))) {
225
            $isIgnore = true;
226
        }
227
        return $isIgnore;
228
    }
229
230
    /**
231
     * Fill list of relevant packages with there require packages
232
     * @param $packages Composer lock file object
233
     */
234
    private function packagesFill($packages)
235
    {
236
        // Get included packages list
237
        $includePackages = $this->includeList($packages);
238
239
        // Create list of relevant packages with there require packages
240
        foreach ($packages as $package) {
241
            $requirement = $package['name'];
242
            if (in_array($requirement, $includePackages)) {
243
                $this->packagesList[$requirement] = array();
244
	            $this->packagesListExtra[$requirement] = isset($package['extra'])?$package['extra']:array();
245
                if (isset($package['require'])) {
246
                    $this->packagesList[$requirement] = array_intersect(array_keys($package['require']), $includePackages);
247
                }
248
            }
249
        }
250
    }
251
252
    private function readFile($path)
253
    {
254
        $packages = array();
255
        // If we have composer configuration file
256
        if (file_exists($path)) {
257
            // Read file into object
258
            $composerObject = json_decode(file_get_contents($path), true);
259
260
            // Gather all possible packages
261
            $packages = array_merge(
262
                array(),
263
                isset($composerObject['packages']) ? $composerObject['packages'] : array(),
264
                isset($composerObject['packages-dev']) ? $composerObject['packages-dev'] : array()
265
            );
266
        }
267
        return $packages;
268
    }
269
270
    /**
271
     * Create list of of required packages
272
     * @param null $includeModule Dependent package
273
     * @param array $ignoreModules
274
     *
275
     * @return array required packages
276
     */
277
    private function getRequiredList($includeModule = null, $ignoreModules = array())
278
    {
279
        $list = isset($includeModule)?$this->packagesList[$includeModule]:$this->packagesList;
280
        $ignoreList = array();
281
        foreach ($ignoreModules as $module) {
282
            $ignoreList[] = $module;
283
            if (is_array($this->packagesList[$module])) {
284
                $ignoreList = array_merge($ignoreList, $this->packagesList[$module]);
285
            }
286
        }
287
288
        $result = array();
289
        foreach ($list as $k=>$v) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "=>"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "=>"; 0 found
Loading history...
290
            $module = is_array($v)?$k:$v;
291 View Code Duplication
            if (!in_array($module, $ignoreList)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
292
                $result[] = $module;
293
                $moduleList = $this->getReqList($this->packagesList[$module]);
294
                $result = array_merge($result, $moduleList);
295
            }
296
        }
297
        return array_values(array_unique($result));
298
    }
299
300
    /**
301
     * Recursive function that get list of required packages
302
     * @param $list List of packages
303
     * @param array $result
304
     *
305
     * @return array required packages
306
     */
307
    private function getReqList($list, $result = array()) {
308
        $return = array();
309
        if (is_array($list)) {
310
            foreach ($list as $module) {
311 View Code Duplication
                if (!in_array($module, $result)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
312
                    $getList = $this->getReqList($this->packagesList[$module], $return);
313
                    $return[] = $module;
314
                    $return = array_merge($return, $getList);
315
                }
316
            }
317
        }
318
        return $return;
319
    }
320
321
	/**
322
	 * Clear object parameters
323
	 */
324
	public function clear()
325
	{
326
		$this->vendorsList = array();
327
		$this->ignoreKey = null;
328
		$this->includeKey = null;
329
		$this->ignorePackages = array();
330
		$this->packagesList = array();
331
	}
332
333
    /**
334
     * Insert a key after a specific key in an array.  If key doesn't exist, value is appended
335
     * to the end of the array.
336
     *
337
     * @param array $array
0 ignored issues
show
Bug introduced by
There is no parameter named $array. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
338
     * @param string $key
339
     * @param integer $newKey
340
     *
341
     * @return array
342
     */
343
    public function insertKeyAfter( array $list, $key, $newKey )
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between opening bracket and type hint "array"; 1 found
Loading history...
Coding Style introduced by
Expected 0 spaces between argument "$newKey" and closing bracket; 1 found
Loading history...
344
    {
345
        $keys = array_keys($list);
346
        $index = array_search( $key, $keys );
347
        $pos = false === $index ? count($list) : $index + 1;
348
        return array_merge(array_slice($list, 0, $pos), array($newKey=>1), array_slice($list, $pos));
349
    }
350
351
    /**
352
     * Insert a key before a specific key in an array.  If key doesn't exist, value is prepended
353
     * to the beginning of the array.
354
     *
355
     * @param array $array
0 ignored issues
show
Bug introduced by
There is no parameter named $array. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
356
     * @param string $key
357
     * @param integer $newKey
358
     *
359
     * @return array
360
     */
361
    public function insertKeyBefore(array $list, $key, $newKey)
362
    {
363
        $keys = array_keys($list);
364
        $pos = (int) array_search($key, $keys);
365
        return array_merge(array_slice($list, 0, $pos), array($newKey=>1), array_slice($list, $pos));
366
    }
367
368
}
369