Passed
Push — master ( a862c8...fef60d )
by Siad
05:54
created

PharPackageTask   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 401
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 135
c 0
b 0
f 0
dl 0
loc 401
ccs 0
cts 140
cp 0
rs 8.64
wmc 47

15 Methods

Rating   Name   Duplication   Size   Complexity  
A setCompression() 0 14 3
A setCliStub() 0 3 1
A setStub() 0 3 1
A setWebStub() 0 3 1
A setAlias() 0 3 1
A setKey() 0 3 1
A setKeyPassword() 0 3 1
A setSignature() 0 23 6
B main() 0 72 7
A createFileSet() 0 6 1
C checkPreconditions() 0 51 14
B buildPhar() 0 35 7
A setDestFile() 0 3 1
A createMetadata() 0 3 1
A setBaseDir() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like PharPackageTask often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PharPackageTask, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * Package task for {@link http://www.php.net/manual/en/book.phar.php Phar technology}.
22
 *
23
 * @package phing.tasks.ext
24
 * @author  Alexey Shockov <[email protected]>
25
 * @since   2.4.0
26
 */
27
class PharPackageTask extends MatchingTask
28
{
29
    /**
30
     * @var PhingFile
31
     */
32
    private $destinationFile;
33
34
    /**
35
     * @var int
36
     */
37
    private $compression = Phar::NONE;
38
39
    /**
40
     * Base directory, from where local package paths will be calculated.
41
     *
42
     * @var PhingFile
43
     */
44
    private $baseDirectory;
45
46
    /**
47
     * @var PhingFile
48
     */
49
    private $cliStubFile;
50
51
    /**
52
     * @var PhingFile
53
     */
54
    private $webStubFile;
55
56
    /**
57
     * @var string
58
     */
59
    private $stubPath;
60
61
    /**
62
     * Private key the Phar will be signed with.
63
     *
64
     * @var PhingFile
65
     */
66
    private $key;
67
68
    /**
69
     * Password for the private key.
70
     *
71
     * @var string
72
     */
73
    private $keyPassword = '';
74
75
    /**
76
     * @var int
77
     */
78
    private $signatureAlgorithm = Phar::SHA1;
79
80
    /**
81
     * @var array
82
     */
83
    private $filesets = [];
84
85
    /**
86
     * @var PharMetadata
87
     */
88
    private $metadata = null;
89
90
    /**
91
     * @var string
92
     */
93
    private $alias;
94
95
    /**
96
     * @return PharMetadata
97
     */
98
    public function createMetadata()
99
    {
100
        return ($this->metadata = new PharMetadata());
101
    }
102
103
    /**
104
     * @return FileSet
105
     */
106
    public function createFileSet()
107
    {
108
        $this->fileset = new FileSet();
109
        $this->filesets[] = $this->fileset;
110
111
        return $this->fileset;
112
    }
113
114
    /**
115
     * Signature algorithm (md5, sha1, sha256, sha512, openssl),
116
     * used for this package.
117
     *
118
     * @param string $algorithm
119
     */
120
    public function setSignature($algorithm)
121
    {
122
        /*
123
         * If we don't support passed algprithm, leave old one.
124
         */
125
        switch ($algorithm) {
126
            case 'md5':
127
                $this->signatureAlgorithm = Phar::MD5;
128
                break;
129
            case 'sha1':
130
                $this->signatureAlgorithm = Phar::SHA1;
131
                break;
132
            case 'sha256':
133
                $this->signatureAlgorithm = Phar::SHA256;
134
                break;
135
            case 'sha512':
136
                $this->signatureAlgorithm = Phar::SHA512;
137
                break;
138
            case 'openssl':
139
                $this->signatureAlgorithm = Phar::OPENSSL;
140
                break;
141
            default:
142
                break;
143
        }
144
    }
145
146
    /**
147
     * Compression type (gzip, bzip2, none) to apply to the packed files.
148
     *
149
     * @param string $compression
150
     */
151
    public function setCompression($compression)
152
    {
153
        /**
154
         * If we don't support passed compression, leave old one.
155
         */
156
        switch ($compression) {
157
            case 'gzip':
158
                $this->compression = Phar::GZ;
159
                break;
160
            case 'bzip2':
161
                $this->compression = Phar::BZ2;
162
                break;
163
            default:
164
                break;
165
        }
166
    }
167
168
    /**
169
     * Destination (output) file.
170
     *
171
     * @param PhingFile $destinationFile
172
     */
173
    public function setDestFile(PhingFile $destinationFile)
174
    {
175
        $this->destinationFile = $destinationFile;
176
    }
177
178
    /**
179
     * Base directory, which will be deleted from each included file (from path).
180
     * Paths with deleted basedir part are local paths in package.
181
     *
182
     * @param PhingFile $baseDirectory
183
     */
184
    public function setBaseDir(PhingFile $baseDirectory)
185
    {
186
        $this->baseDirectory = $baseDirectory;
187
    }
188
189
    /**
190
     * Relative path within the phar package to run,
191
     * if accessed on the command line.
192
     *
193
     * @param PhingFile $stubFile
194
     */
195
    public function setCliStub(PhingFile $stubFile)
196
    {
197
        $this->cliStubFile = $stubFile;
198
    }
199
200
    /**
201
     * Relative path within the phar package to run,
202
     * if accessed through a web browser.
203
     *
204
     * @param PhingFile $stubFile
205
     */
206
    public function setWebStub(PhingFile $stubFile)
207
    {
208
        $this->webStubFile = $stubFile;
209
    }
210
211
    /**
212
     * A path to a php file that contains a custom stub.
213
     *
214
     * @param string $stubPath
215
     */
216
    public function setStub($stubPath)
217
    {
218
        $this->stubPath = $stubPath;
219
    }
220
221
    /**
222
     * An alias to assign to the phar package.
223
     *
224
     * @param string $alias
225
     */
226
    public function setAlias($alias)
227
    {
228
        $this->alias = $alias;
229
    }
230
231
    /**
232
     * Sets the private key to use to sign the Phar with.
233
     *
234
     * @param PhingFile $key Private key to sign the Phar with.
235
     */
236
    public function setKey(PhingFile $key)
237
    {
238
        $this->key = $key;
239
    }
240
241
    /**
242
     * Password for the private key.
243
     *
244
     * @param string $keyPassword
245
     */
246
    public function setKeyPassword($keyPassword)
247
    {
248
        $this->keyPassword = $keyPassword;
249
    }
250
251
    /**
252
     * @throws BuildException
253
     */
254
    public function main()
255
    {
256
        $this->checkPreconditions();
257
258
        try {
259
            $this->log(
260
                'Building package: ' . $this->destinationFile->__toString(),
261
                Project::MSG_INFO
262
            );
263
264
            /**
265
             * Delete old package, if exists.
266
             */
267
            if ($this->destinationFile->exists()) {
268
                /**
269
                 * TODO Check operation for errors...
270
                 */
271
                $this->destinationFile->delete();
272
            }
273
274
            $phar = $this->buildPhar();
275
            $phar->startBuffering();
276
277
            $baseDirectory = realpath($this->baseDirectory->getPath());
278
279
            foreach ($this->filesets as $fileset) {
280
                $this->log(
281
                    'Adding specified files in ' . $fileset->getDir($this->project) . ' to package',
282
                    Project::MSG_VERBOSE
283
                );
284
285
                $phar->buildFromIterator($fileset, $baseDirectory);
286
            }
287
288
            $phar->stopBuffering();
289
290
            /**
291
             * File compression, if needed.
292
             */
293
            if (Phar::NONE != $this->compression) {
294
                $phar->compressFiles($this->compression);
295
            }
296
297
            if ($this->signatureAlgorithm == Phar::OPENSSL) {
298
                // Load up the contents of the key
299
                $keyContents = file_get_contents($this->key);
300
301
                // Attempt to load the given key as a PKCS#12 Cert Store first.
302
                if (openssl_pkcs12_read($keyContents, $certs, $this->keyPassword)) {
303
                    $private = openssl_pkey_get_private($certs['pkey']);
304
                } else {
305
                    // Fall back to a regular PEM-encoded private key.
306
                    // Setup an OpenSSL resource using the private key
307
                    // and tell the Phar to sign it using that key.
308
                    $private = openssl_pkey_get_private($keyContents, $this->keyPassword);
309
                }
310
311
                openssl_pkey_export($private, $pkey);
312
                $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
313
314
                // Get the details so we can get the public key and write that out
315
                // alongside the phar.
316
                $details = openssl_pkey_get_details($private);
0 ignored issues
show
Bug introduced by
It seems like $private can also be of type false; however, parameter $key of openssl_pkey_get_details() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

316
                $details = openssl_pkey_get_details(/** @scrutinizer ignore-type */ $private);
Loading history...
317
                file_put_contents($this->destinationFile . '.pubkey', $details['key']);
318
            } else {
319
                $phar->setSignatureAlgorithm($this->signatureAlgorithm);
320
            }
321
        } catch (Exception $e) {
322
            throw new BuildException(
323
                'Problem creating package: ' . $e->getMessage(),
324
                $e,
325
                $this->getLocation()
326
            );
327
        }
328
    }
329
330
    /**
331
     * @throws BuildException
332
     */
333
    private function checkPreconditions()
334
    {
335
        if (ini_get('phar.readonly') == "1") {
336
            throw new BuildException(
337
                "PharPackageTask require phar.readonly php.ini setting to be disabled"
338
            );
339
        }
340
341
        if (!extension_loaded('phar')) {
342
            throw new BuildException(
343
                "PharPackageTask require either PHP 5.3 or better or the PECL's Phar extension"
344
            );
345
        }
346
347
        if (null === $this->destinationFile) {
348
            throw new BuildException("destfile attribute must be set!", $this->getLocation());
349
        }
350
351
        if ($this->destinationFile->exists() && $this->destinationFile->isDirectory()) {
352
            throw new BuildException("destfile is a directory!", $this->getLocation());
353
        }
354
355
        if (!$this->destinationFile->canWrite()) {
356
            throw new BuildException("Can not write to the specified destfile!", $this->getLocation());
357
        }
358
        if (null !== $this->baseDirectory) {
359
            if (!$this->baseDirectory->exists()) {
360
                throw new BuildException(
361
                    "basedir '" . (string) $this->baseDirectory . "' does not exist!",
362
                    $this->getLocation()
363
                );
364
            }
365
        }
366
        if ($this->signatureAlgorithm == Phar::OPENSSL) {
367
            if (!extension_loaded('openssl')) {
368
                throw new BuildException(
369
                    "PHP OpenSSL extension is required for OpenSSL signing of Phars!",
370
                    $this->getLocation()
371
                );
372
            }
373
374
            if (null === $this->key) {
375
                throw new BuildException("key attribute must be set for OpenSSL signing!", $this->getLocation());
376
            }
377
378
            if (!$this->key->exists()) {
379
                throw new BuildException("key '" . (string) $this->key . "' does not exist!", $this->getLocation());
380
            }
381
382
            if (!$this->key->canRead()) {
383
                throw new BuildException("key '" . (string) $this->key . "' cannot be read!", $this->getLocation());
384
            }
385
        }
386
    }
387
388
    /**
389
     * Build and configure Phar object.
390
     *
391
     * @return Phar
392
     */
393
    private function buildPhar()
394
    {
395
        $phar = new Phar($this->destinationFile);
396
397
        if (!empty($this->stubPath)) {
398
            $phar->setStub(file_get_contents($this->stubPath));
399
        } else {
400
            if (!empty($this->cliStubFile)) {
401
                $cliStubFile = str_replace('\\', '/', $this->cliStubFile->getPathWithoutBase($this->baseDirectory));
402
            } else {
403
                $cliStubFile = null;
404
            }
405
406
            if (!empty($this->webStubFile)) {
407
                $webStubFile = str_replace('\\', '/', $this->webStubFile->getPathWithoutBase($this->baseDirectory));
408
            } else {
409
                $webStubFile = null;
410
            }
411
412
            $phar->setDefaultStub($cliStubFile, $webStubFile);
413
        }
414
415
        if ($this->metadata === null) {
416
            $this->createMetaData();
417
        }
418
419
        if ($metadata = $this->metadata->toArray()) {
420
            $phar->setMetadata($metadata);
421
        }
422
423
        if (!empty($this->alias)) {
424
            $phar->setAlias($this->alias);
425
        }
426
427
        return $phar;
428
    }
429
}
430