1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Drush Cerbere command line tools. |
5
|
|
|
* Copyright (C) 2015 - Sebastien Malot <[email protected]> |
6
|
|
|
* |
7
|
|
|
* This program is free software; you can redistribute it and/or modify |
8
|
|
|
* it under the terms of the GNU General Public License as published by |
9
|
|
|
* the Free Software Foundation; either version 2 of the License, or |
10
|
|
|
* (at your option) any later version. |
11
|
|
|
* |
12
|
|
|
* This program is distributed in the hope that it will be useful, |
13
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15
|
|
|
* GNU General Public License for more details. |
16
|
|
|
* |
17
|
|
|
* You should have received a copy of the GNU General Public License along |
18
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc., |
19
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
20
|
|
|
*/ |
21
|
|
|
|
22
|
|
|
namespace Cerbere\Model; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Class Project |
26
|
|
|
* |
27
|
|
|
* @package Cerbere\Model |
28
|
|
|
*/ |
29
|
|
|
class Project |
30
|
|
|
{ |
31
|
|
|
/** |
32
|
|
|
* URL to check for updates, if a given project doesn't define its own. |
33
|
|
|
*/ |
34
|
|
|
const UPDATE_DEFAULT_URL = 'http://updates.drupal.org/release-history'; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* |
38
|
|
|
*/ |
39
|
|
|
const INSTALL_TYPE_OFFICIAL = 'official'; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* |
43
|
|
|
*/ |
44
|
|
|
const INSTALL_TYPE_DEV = 'dev'; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* |
48
|
|
|
*/ |
49
|
|
|
const INSTALL_TYPE_UNKNOWN = 'unknown'; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* |
53
|
|
|
*/ |
54
|
|
|
const TYPE_PROJECT_DISTRIBUTION = 'project_distribution'; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* |
58
|
|
|
*/ |
59
|
|
|
const TYPE_PROJECT_CORE = 'project_core'; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* |
63
|
|
|
*/ |
64
|
|
|
const TYPE_PROJECT_MODULE = 'project_module'; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* |
68
|
|
|
*/ |
69
|
|
|
const TYPE_PROJECT_THEME = 'project_theme'; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* |
73
|
|
|
*/ |
74
|
|
|
const TYPE_UNKNOWN = 'unknown'; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @var |
78
|
|
|
*/ |
79
|
|
|
protected $project; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @var string |
83
|
|
|
*/ |
84
|
|
|
protected $filename; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @var |
88
|
|
|
*/ |
89
|
|
|
protected $name; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @var |
93
|
|
|
*/ |
94
|
|
|
protected $core; |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @var |
98
|
|
|
*/ |
99
|
|
|
protected $version; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @var |
103
|
|
|
*/ |
104
|
|
|
protected $status_url; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @var |
108
|
|
|
*/ |
109
|
|
|
protected $install_type; |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @var |
113
|
|
|
*/ |
114
|
|
|
protected $existing_version; |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @var |
118
|
|
|
*/ |
119
|
|
|
protected $existing_major; |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @var array |
123
|
|
|
*/ |
124
|
|
|
protected $data; |
125
|
|
|
|
126
|
|
|
// Calculated properties. |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* @var |
130
|
|
|
*/ |
131
|
|
|
protected $status; |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @var |
135
|
|
|
*/ |
136
|
|
|
protected $project_status; |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @var string |
140
|
|
|
*/ |
141
|
|
|
protected $project_type; |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* @var |
145
|
|
|
*/ |
146
|
|
|
protected $reason; |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @var |
150
|
|
|
*/ |
151
|
|
|
protected $fetch_status; |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* @var |
155
|
|
|
*/ |
156
|
|
|
protected $latest_version; |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @var |
160
|
|
|
*/ |
161
|
|
|
protected $latest_dev; |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @var |
165
|
|
|
*/ |
166
|
|
|
protected $dev_version; |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @var |
170
|
|
|
*/ |
171
|
|
|
protected $recommended; |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @var |
175
|
|
|
*/ |
176
|
|
|
protected $datestamp; |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @var array |
180
|
|
|
*/ |
181
|
|
|
protected $releases; |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* @var array |
185
|
|
|
*/ |
186
|
|
|
protected $security_updates; |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @var array |
190
|
|
|
*/ |
191
|
|
|
protected $also_available; |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @param string $project |
195
|
|
|
* @param string $core |
196
|
|
|
* @param string $version |
197
|
|
|
* @param \DateTime|int|null $date |
198
|
|
|
*/ |
199
|
|
|
public function __construct($project, $core, $version, $date = null) |
200
|
|
|
{ |
201
|
|
|
$this->project = $project; |
202
|
|
|
$this->name = $project; |
203
|
|
|
$this->core = $core; |
204
|
|
|
$this->version = $version; |
205
|
|
|
|
206
|
|
|
if ($date instanceof \DateTime) { |
207
|
|
|
$this->datestamp = $date->getTimestamp(); |
208
|
|
|
} elseif (is_int($date)) { |
209
|
|
|
$this->datestamp = $date; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
$this->project_type = self::TYPE_UNKNOWN; |
213
|
|
|
|
214
|
|
|
$this->releases = array(); |
215
|
|
|
$this->security_updates = array(); |
216
|
|
|
$this->also_available = array(); |
217
|
|
|
|
218
|
|
|
$this->init(); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* @return string |
223
|
|
|
*/ |
224
|
|
|
public function getFilename() |
225
|
|
|
{ |
226
|
|
|
return $this->filename; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* @param string $filename |
231
|
|
|
*/ |
232
|
|
|
public function setFilename($filename) |
233
|
|
|
{ |
234
|
|
|
$this->filename = $filename; |
235
|
|
|
|
236
|
|
|
$this->init(); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* @return string |
241
|
|
|
*/ |
242
|
|
|
public function getWorkingDirectory() |
243
|
|
|
{ |
244
|
|
|
if ($this->getProject() == 'drupal' && $this->getCore() == '8.x') { |
245
|
|
|
$path = str_replace('\\', '/', dirname($this->getFilename())); |
246
|
|
|
|
247
|
|
|
// Todo: Optimize. |
248
|
|
View Code Duplication |
if (($position = strpos($path, '/core/modules/')) !== false) { |
|
|
|
|
249
|
|
|
return substr($path, 0, $position); |
250
|
|
|
} |
251
|
|
View Code Duplication |
if (($position = strpos($path, '/core/profiles/')) !== false) { |
|
|
|
|
252
|
|
|
return substr($path, 0, $position); |
253
|
|
|
} |
254
|
|
View Code Duplication |
if (($position = strpos($path, '/core/themes/')) !== false) { |
|
|
|
|
255
|
|
|
return substr($path, 0, $position); |
256
|
|
|
} |
257
|
|
View Code Duplication |
if (($position = strpos($path, '/core/tests/')) !== false) { |
|
|
|
|
258
|
|
|
return substr($path, 0, $position); |
259
|
|
|
} |
260
|
|
|
} elseif ($this->getProject() == 'drupal' && $this->getCore() == '7.x') { |
261
|
|
|
return dirname($this->getFilename()) . '/../..'; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
return dirname($this->getFilename()); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* |
269
|
|
|
*/ |
270
|
|
|
protected function init() |
271
|
|
|
{ |
272
|
|
|
// Patch project if Drupal detected. |
273
|
|
|
if (isset($this->data['package']) && strtolower($this->data['package']) == 'core') { |
274
|
|
|
$this->name = 'Drupal'; |
275
|
|
|
|
276
|
|
|
$this->data = array( |
277
|
|
|
'name' => $this->name, |
278
|
|
|
'description' => '', |
279
|
|
|
'package' => $this->data['package'], |
280
|
|
|
'core' => $this->data['core'], |
281
|
|
|
'version' => $this->data['version'], |
282
|
|
|
'files' => array(), |
283
|
|
|
'configure' => '', |
284
|
|
|
'project' => 'drupal', |
285
|
|
|
'datestamp' => $this->data['datestamp'], |
286
|
|
|
); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
$this->status_url = self::UPDATE_DEFAULT_URL; |
290
|
|
|
|
291
|
|
|
// Assume an official release until we see otherwise. |
292
|
|
|
$this->install_type = self::INSTALL_TYPE_OFFICIAL; |
293
|
|
|
$this->existing_version = $this->version; |
294
|
|
|
|
295
|
|
|
if (isset($this->version)) { |
296
|
|
|
// Check for development snapshots |
297
|
|
|
if (preg_match('/(dev|HEAD)/', $this->version)) { |
298
|
|
|
$this->install_type = self::INSTALL_TYPE_DEV; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
// Figure out what the currently installed major version is. We need |
302
|
|
|
// to handle both contribution (e.g. "5.x-1.3", major = 1) and core |
303
|
|
|
// (e.g. "5.1", major = 5) version strings. |
304
|
|
|
$matches = array(); |
305
|
|
|
if (preg_match('/^(\d+\.x-)?(\d+)\..*$/', $this->version, $matches)) { |
306
|
|
|
$this->existing_major = $matches[2]; |
307
|
|
|
} else { |
308
|
|
|
// This would only happen for version strings that don't follow the |
309
|
|
|
// drupal.org convention. We let contribs define "major" in their |
310
|
|
|
// .info in this case, and only if that's missing would we hit this. |
311
|
|
|
$this->existing_major = -1; |
312
|
|
|
} |
313
|
|
|
} else { |
314
|
|
|
// No version info available at all. |
315
|
|
|
$this->install_type = self::INSTALL_TYPE_UNKNOWN; |
316
|
|
|
$this->existing_version = 'Unknown'; |
317
|
|
|
$this->existing_major = -1; |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* @param string $version |
323
|
|
|
* @param Release $release |
324
|
|
|
*/ |
325
|
|
|
public function addSecurityUpdate($version, $release) |
326
|
|
|
{ |
327
|
|
|
$this->security_updates[$version] = $release; |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* @param string $version_major |
332
|
|
|
* @param string $version |
333
|
|
|
*/ |
334
|
|
|
public function addAlsoAvailable($version_major, $version) |
335
|
|
|
{ |
336
|
|
|
$this->also_available[$version_major] = $version; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* @return array |
341
|
|
|
*/ |
342
|
|
|
public function getAlsoAvailable() |
343
|
|
|
{ |
344
|
|
|
return $this->also_available; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* @param string $version_major |
349
|
|
|
* @return bool |
350
|
|
|
*/ |
351
|
|
|
public function hasAlsoAvailable($version_major) |
352
|
|
|
{ |
353
|
|
|
return isset($this->also_available[$version_major]); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* @return mixed |
358
|
|
|
*/ |
359
|
|
|
public function getCore() |
360
|
|
|
{ |
361
|
|
|
return $this->core; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* @return mixed |
366
|
|
|
*/ |
367
|
|
|
public function getDatestamp() |
368
|
|
|
{ |
369
|
|
|
return $this->datestamp; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* @return array |
374
|
|
|
*/ |
375
|
|
|
public function getDetails() |
376
|
|
|
{ |
377
|
|
|
return $this->data; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* @return string |
382
|
|
|
*/ |
383
|
|
|
public function getProjectType() |
384
|
|
|
{ |
385
|
|
|
return $this->project_type; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* @param string $type |
390
|
|
|
*/ |
391
|
|
|
public function setProjectType($type) |
392
|
|
|
{ |
393
|
|
|
$this->project_type = $type; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* @return mixed |
398
|
|
|
*/ |
399
|
|
|
public function getDevVersion() |
400
|
|
|
{ |
401
|
|
|
return $this->dev_version; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* @param $dev_version |
406
|
|
|
*/ |
407
|
|
|
public function setDevVersion($dev_version) |
408
|
|
|
{ |
409
|
|
|
$this->dev_version = $dev_version; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* @return string |
414
|
|
|
*/ |
415
|
|
|
public function getExistingMajor() |
416
|
|
|
{ |
417
|
|
|
return $this->existing_major; |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* @return string |
422
|
|
|
*/ |
423
|
|
|
public function getExistingVersion() |
424
|
|
|
{ |
425
|
|
|
return $this->existing_version; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* @return integer |
430
|
|
|
*/ |
431
|
|
|
public function getFetchStatus() |
432
|
|
|
{ |
433
|
|
|
return $this->fetch_status; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* @param $fetch_status |
438
|
|
|
*/ |
439
|
|
|
public function setFetchStatus($fetch_status) |
440
|
|
|
{ |
441
|
|
|
$this->fetch_status = $fetch_status; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* @return string |
446
|
|
|
*/ |
447
|
|
|
public function getInstallType() |
448
|
|
|
{ |
449
|
|
|
return $this->install_type; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* @return mixed |
454
|
|
|
*/ |
455
|
|
|
public function getLatestDev() |
456
|
|
|
{ |
457
|
|
|
return $this->latest_dev; |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* @param $latest_dev |
462
|
|
|
*/ |
463
|
|
|
public function setLatestDev($latest_dev) |
464
|
|
|
{ |
465
|
|
|
$this->latest_dev = $latest_dev; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* @return mixed |
470
|
|
|
*/ |
471
|
|
|
public function getLatestVersion() |
472
|
|
|
{ |
473
|
|
|
return $this->latest_version; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
/** |
477
|
|
|
* @param $latest_version |
478
|
|
|
*/ |
479
|
|
|
public function setLatestVersion($latest_version) |
480
|
|
|
{ |
481
|
|
|
$this->latest_version = $latest_version; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* @return string |
486
|
|
|
*/ |
487
|
|
|
public function getName() |
488
|
|
|
{ |
489
|
|
|
return $this->name; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* @return string |
494
|
|
|
*/ |
495
|
|
|
public function getProject() |
496
|
|
|
{ |
497
|
|
|
return $this->project; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* @return int |
502
|
|
|
*/ |
503
|
|
|
public function getProjectStatus() |
504
|
|
|
{ |
505
|
|
|
return $this->project_status; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* @param int $project_status |
510
|
|
|
*/ |
511
|
|
|
public function setProjectStatus($project_status) |
512
|
|
|
{ |
513
|
|
|
$this->project_status = $project_status; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* @return string |
518
|
|
|
*/ |
519
|
|
|
public function getReason() |
520
|
|
|
{ |
521
|
|
|
return $this->reason; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* @param string $reason |
526
|
|
|
*/ |
527
|
|
|
public function setReason($reason) |
528
|
|
|
{ |
529
|
|
|
$this->reason = $reason; |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* @return mixed |
534
|
|
|
*/ |
535
|
|
|
public function getRecommended() |
536
|
|
|
{ |
537
|
|
|
return $this->recommended; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
/** |
541
|
|
|
* @param $recommended |
542
|
|
|
*/ |
543
|
|
|
public function setRecommended($recommended) |
544
|
|
|
{ |
545
|
|
|
$this->recommended = $recommended; |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* @return Release[] |
550
|
|
|
*/ |
551
|
|
|
public function getReleases() |
552
|
|
|
{ |
553
|
|
|
return $this->releases; |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* @param Release[] $releases |
558
|
|
|
*/ |
559
|
|
|
public function setReleases($releases) |
560
|
|
|
{ |
561
|
|
|
$this->releases = $releases; |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* @param string $release |
566
|
|
|
* |
567
|
|
|
* @return Release|false |
568
|
|
|
*/ |
569
|
|
|
public function getRelease($release) |
570
|
|
|
{ |
571
|
|
|
if (isset($this->releases[$release])) { |
572
|
|
|
return $this->releases[$release]; |
573
|
|
|
} |
574
|
|
|
|
575
|
|
|
return false; |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
/** |
579
|
|
|
* @return Release[] |
580
|
|
|
*/ |
581
|
|
|
public function getSecurityUpdates() |
582
|
|
|
{ |
583
|
|
|
return $this->security_updates; |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* @param Release[] $security_updates |
588
|
|
|
*/ |
589
|
|
|
public function setSecurityUpdates($security_updates) |
590
|
|
|
{ |
591
|
|
|
$this->security_updates = $security_updates; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
/** |
595
|
|
|
* @return int |
596
|
|
|
*/ |
597
|
|
|
public function getStatus() |
598
|
|
|
{ |
599
|
|
|
return $this->status; |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* @param int $status |
604
|
|
|
*/ |
605
|
|
|
public function setStatus($status) |
606
|
|
|
{ |
607
|
|
|
$this->status = $status; |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
/** |
611
|
|
|
* @return string |
612
|
|
|
*/ |
613
|
|
|
public function getStatusUrl() |
614
|
|
|
{ |
615
|
|
|
return $this->status_url; |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
/** |
619
|
|
|
* @param string $status_url |
620
|
|
|
*/ |
621
|
|
|
public function setStatusUrl($status_url) |
622
|
|
|
{ |
623
|
|
|
$this->status_url = $status_url; |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
/** |
627
|
|
|
* @return string |
628
|
|
|
*/ |
629
|
|
|
public function getVersion() |
630
|
|
|
{ |
631
|
|
|
return $this->version; |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
/** |
635
|
|
|
* @return bool |
636
|
|
|
*/ |
637
|
|
|
public function hasSecurityUpdates() |
638
|
|
|
{ |
639
|
|
|
return count($this->security_updates) > 0; |
640
|
|
|
} |
641
|
|
|
|
642
|
|
|
/** |
643
|
|
|
* @param array $data |
644
|
|
|
*/ |
645
|
|
|
public function setDetails($data) |
646
|
|
|
{ |
647
|
|
|
$this->data = $data; |
648
|
|
|
|
649
|
|
|
foreach (array('name', 'core', 'version', 'datestamp') as $property) { |
650
|
|
|
if (isset($data[$property])) { |
651
|
|
|
$this->$property = $data[$property]; |
652
|
|
|
} |
653
|
|
|
} |
654
|
|
|
|
655
|
|
|
$this->init(); |
656
|
|
|
} |
657
|
|
|
|
658
|
|
|
/** |
659
|
|
|
* @param string $version |
660
|
|
|
* @param Release $release |
661
|
|
|
*/ |
662
|
|
|
public function setRelease($version, Release $release) |
663
|
|
|
{ |
664
|
|
|
$this->releases[$version] = $release; |
665
|
|
|
} |
666
|
|
|
} |
667
|
|
|
|
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.