Completed
Push — master ( 8a1ebe...a957b4 )
by Sam
45:51
created

AddonUpdater::updateCompatibility()   D

Complexity

Conditions 10
Paths 21

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 20
nc 21
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
use Composer\Package\AliasPackage;
4
use Composer\Package\CompletePackage;
5
use Guzzle\Http\Exception\ClientErrorResponseException;
6
use SilverStripe\Elastica\ElasticaService;
7
use Packagist\Api\Result\Package;
8
use Composer\Package\Version\VersionParser;
9
10
/**
11
 * Updates all add-ons from Packagist.
12
 */
13
class AddonUpdater {
14
15
	/**
16
	 * @var PackagistService
17
	 */
18
	private $packagist;
19
20
	/**
21
	 * @var SilverStripe\Elastica\ElasticaService
22
	 */
23
	private $elastica;
24
25
	/**
26
	 * @var ResqueService
27
	 */
28
	private $resque;
29
30
	/**
31
	 * @var SilverStripeVersion[]
32
	 */
33
	private $silverstripes;
34
35
	public function __construct(
36
		PackagistService $packagist,
37
		ElasticaService $elastica,
38
		ResqueService $resque
39
	) {
40
		$this->packagist = $packagist;
41
		$this->elastica = $elastica;
42
		$this->resque = $resque;
43
44
        $this->setSilverStripeVersions(SilverStripeVersion::get());
45
	}
46
47
	/**
48
	 * Updates all add-ons.
49
	 *
50
	 * @param Boolean Clear existing addons before updating them.
51
	 * Will also clear their search index, and cascade the delete for associated data.
52
	 * @param Array Limit to specific addons, using their name incl. vendor prefix.
53
	 */
54
	public function update($clear = false, $limitAddons = null)
55
	{
56
		if ($clear && !$limitAddons) {
57
			Addon::get()->removeAll();
58
			AddonAuthor::get()->removeAll();
59
			AddonKeyword::get()->removeAll();
60
			AddonLink::get()->removeAll();
61
			AddonVendor::get()->removeAll();
62
			AddonVersion::get()->removeAll();
63
		}
64
65
		// This call to packagist can be expensive. Requests are served from a cache if usePackagistCache() returns true
66
		$cache = SS_Cache::factory('addons');
67
		if ($this->usePackagistCache() && $packages = $cache->load('packagist')) {
68
			$packages = unserialize($packages);
69
		} else {
70
			$packages = $this->packagist->getPackages();
71
			$cache->save(serialize($packages), 'packagist');
72
		}
73
		$this->elastica->startBulkIndex();
74
75
		foreach ($packages as $package) {
76
			/** @var Packagist\Api\Result\Package $package */
77
78
			$isAbandoned = (property_exists($package, 'abandoned') && $package->abandoned);
0 ignored issues
show
Bug introduced by
The property abandoned does not seem to exist in Packagist\Api\Result\Package.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
79
			$name = $package->getName();
80
			$versions = $package->getVersions();
81
82
			if ($limitAddons && !in_array($name, $limitAddons)) {
83
				continue;
84
			}
85
86
			$addon = Addon::get()->filter('Name', $name)->first();
87
88
			if (!$addon) {
89
				if ($isAbandoned) {
90
					echo ' - Skipping abandoned package: ' . $name, PHP_EOL;
91
					continue;
92
				}
93
94
				$addon = new Addon();
95
				$addon->Name = $name;
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
96
				$addon->write();
97
			}
98
99
			if ($isAbandoned) {
100
				echo ' - Removing abandoned package: ' . $name, PHP_EOL;
101
				$addon->delete();
102
				continue;
103
			}
104
105
			usort($versions, function ($a, $b) {
106
				return version_compare($a->getVersionNormalized(), $b->getVersionNormalized());
107
			});
108
109
			$this->updateAddon($addon, $package, $versions);
0 ignored issues
show
Compatibility introduced by
$addon of type object<DataObject> is not a sub-type of object<Addon>. It seems like you assume a child class of the class DataObject to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
110
		}
111
112
		$this->elastica->endBulkIndex();
113
	}
114
115
116
117
	/**
118
	 * Check whether or not we should contact packagist or use a cached version. This allows to speed up the task
119
	 * during development.
120
	 *
121
	 * @return bool
122
	 */
123
	protected function usePackagistCache() {
124
		return Director::isDev();
125
	}
126
127
	private function updateAddon(Addon $addon, Package $package, array $versions) {
128
		if (!$addon->VendorID) {
0 ignored issues
show
Documentation introduced by
The property VendorID does not exist on object<Addon>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
129
			$vendor = AddonVendor::get()->filter('Name', $addon->VendorName())->first();
130
131
			if (!$vendor) {
132
				$vendor = new AddonVendor();
133
				$vendor->Name = $addon->VendorName();
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<AddonVendor>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
134
				$vendor->write();
135
			}
136
137
			$addon->VendorID = $vendor->ID;
0 ignored issues
show
Documentation introduced by
The property VendorID does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
138
		}
139
140
		$addon->Type = str_replace('silverstripe-', '', $package->getType());
0 ignored issues
show
Documentation introduced by
The property Type does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
141
		$addon->Description = $package->getDescription();
0 ignored issues
show
Documentation introduced by
The property Description does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
142
		$addon->Released = strtotime($package->getTime());
0 ignored issues
show
Documentation introduced by
The property Released does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
143
		$addon->Repository = $package->getRepository();
0 ignored issues
show
Documentation introduced by
The property Repository does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
144
		$addon->Downloads = $package->getDownloads()->getTotal();
0 ignored issues
show
Documentation introduced by
The property Downloads does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
145
		$addon->DownloadsMonthly = $package->getDownloads()->getMonthly();
0 ignored issues
show
Documentation introduced by
The property DownloadsMonthly does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
146
		$addon->Favers = $package->getFavers();
0 ignored issues
show
Documentation introduced by
The property Favers does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
147
148
		foreach ($versions as $version) {
149
			$this->updateVersion($addon, $version);
150
		}
151
152
		// If there is no build, then queue one up if the add-on requires
153
		// one.
154
		if (!$addon->BuildQueued) {
0 ignored issues
show
Documentation introduced by
The property BuildQueued does not exist on object<Addon>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
155
			if (!$addon->BuiltAt) {
0 ignored issues
show
Documentation introduced by
The property BuiltAt does not exist on object<Addon>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
156
				$this->resque->queue('first_build', 'BuildAddonJob', array('id' => $addon->ID));
157
				$addon->BuildQueued = true;
0 ignored issues
show
Documentation introduced by
The property BuildQueued does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
158
			} else {
159
				$built = (int) $addon->obj('BuiltAt')->format('U');
160
161
				foreach ($versions as $version) {
162
					if (strtotime($version->getTime()) > $built) {
163
						$this->resque->queue('update', 'BuildAddonJob', array('id' => $addon->ID));
164
						$addon->BuildQueued = true;
0 ignored issues
show
Documentation introduced by
The property BuildQueued does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
165
166
						break;
167
					}
168
				}
169
			}
170
		}
171
172
		$addon->LastUpdated = time();
0 ignored issues
show
Documentation introduced by
The property LastUpdated does not exist on object<Addon>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
173
		$addon->write();
174
	}
175
176
	private function updateVersion(Addon $addon, Version $package) {
177
		$version = null;
178
179
		if ($addon->isInDB()) {
180
			$version = $addon->Versions()->filter('Version', $package->getVersionNormalized())->first();
0 ignored issues
show
Bug introduced by
The method Versions() does not exist on Addon. Did you maybe mean SortedVersions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
181
		}
182
183
		if (!$version) {
184
			$version = new AddonVersion();
185
		}
186
187
		$version->Name = $package->getName();
188
		$version->Type = str_replace('silverstripe-', '', $package->getType());
189
		$version->Description = $package->getDescription();
190
		$version->Released = strtotime($package->getTime());
191
		$keywords = $package->getKeywords();
192
193
		if ($keywords) {
194
			foreach ($keywords as $keyword) {
195
				$keyword = AddonKeyword::get_by_name($keyword);
196
197
				$addon->Keywords()->add($keyword);
0 ignored issues
show
Documentation Bug introduced by
The method Keywords does not exist on object<Addon>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
198
				$version->Keywords()->add($keyword);
199
			}
200
		}
201
202
		$version->Version = $package->getVersionNormalized();
203
		$version->PrettyVersion = $package->getVersion();
204
205
		$stability = VersionParser::parseStability($package->getVersion());
206
		$isDev = $stability === 'dev';
207
		$version->Development = $isDev;
208
209
		$version->SourceType = $package->getSource()->getType();
210
		$version->SourceUrl = $package->getSource()->getUrl();
211
		$version->SourceReference = $package->getSource()->getReference();
212
213
		if($package->getDist()) {
214
			$version->DistType = $package->getDist()->getType();
215
			$version->DistUrl = $package->getDist()->getUrl();
216
			$version->DistReference = $package->getDist()->getReference();
217
			$version->DistChecksum = $package->getDist()->getShasum();
218
		}
219
220
		$version->Extra = $package->getExtra();
221
		$version->Homepage = $package->getHomepage();
222
		$version->License = $package->getLicense();
223
		// $version->Support = $package->getSupport();
224
225
		$addon->Versions()->add($version);
0 ignored issues
show
Bug introduced by
The method Versions() does not exist on Addon. Did you maybe mean SortedVersions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
226
227
		$this->updateLinks($version, $package);
228
		$this->updateCompatibility($addon, $version, $package);
229
		$this->updateAuthors($version, $package);
230
	}
231
232
	private function updateLinks(AddonVersion $version, CompletePackage $package) {
233
		$getLink = function ($name, $type) use ($version) {
234
			$link = null;
235
236
			if ($version->isInDB()) {
237
				$link = $version->Links()->filter('Name', $name)->filter('Type', $type)->first();
0 ignored issues
show
Documentation Bug introduced by
The method Links does not exist on object<AddonVersion>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
238
			}
239
240
			if (!$link) {
241
				$link = new AddonLink();
242
				$link->Name = $name;
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<AddonLink>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
243
				$link->Type = $type;
0 ignored issues
show
Documentation introduced by
The property Type does not exist on object<AddonLink>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
244
			}
245
246
			return $link;
247
		};
248
249
		$types = array(
250
			'require' => 'getRequire',
251
			'require-dev' => 'getRequireDev',
252
			'provide' => 'getProvide',
253
			'conflict' => 'getConflict',
254
			'replace' => 'getReplace'
255
		);
256
257
		foreach ($types as $type => $method) {
258
			if ($linked = $package->$method()) foreach ($linked as $link => $constraint) {
259
				$name = $link;
260
				$addon = Addon::get()->filter('Name', $name)->first();
261
262
				$local = $getLink($name, $type);
263
				$local->Constraint = $constraint;
264
265
				if ($addon) {
266
					$local->TargetID = $addon->ID;
267
				}
268
269
				$version->Links()->add($local);
0 ignored issues
show
Documentation Bug introduced by
The method Links does not exist on object<AddonVersion>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
270
			}
271
		}
272
273
		//to-do api have no method to get this.
274
		/*$suggested = $package->getSuggests();
275
276
		if ($suggested) foreach ($suggested as $package => $description) {
277
			$link = $getLink($package, 'suggest');
278
			$link->Description = $description;
279
280
			$version->Links()->add($link);
281
		}*/
282
	}
283
284
	private function updateCompatibility(Addon $addon, AddonVersion $version, CompletePackage $package) {
285
		$require = null;
286
287
		if($package->getRequire()) foreach ($package->getRequire() as $name => $link) {
0 ignored issues
show
Bug introduced by
The method getRequire() does not exist on Composer\Package\CompletePackage. Did you maybe mean getRequires()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
288
			if((string)$link == 'self.version') continue;
289
290
			if ($name == 'silverstripe/framework') {
291
				$require = $link;
292
				break;
293
			}
294
295
			if ($name == 'silverstripe/cms') {
296
				$require = $link;
297
			}
298
		}
299
300
		if (!$require) {
301
			return;
302
		}
303
304
		$addon->CompatibleVersions()->removeAll();
0 ignored issues
show
Documentation Bug introduced by
The method CompatibleVersions does not exist on object<Addon>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
305
		$version->CompatibleVersions()->removeAll();
0 ignored issues
show
Documentation Bug introduced by
The method CompatibleVersions does not exist on object<AddonVersion>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
306
307
		foreach ($this->getSilverStripeVersions() as $silverStripeVersion) {
308
            /** @var SilverStripeVersion $silverStripeVersion */
309
			try {
310
                if ($silverStripeVersion->getConstraintValidity($require)) {
311
					$addon->CompatibleVersions()->add($silverStripeVersion);
0 ignored issues
show
Documentation Bug introduced by
The method CompatibleVersions does not exist on object<Addon>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
312
					$version->CompatibleVersions()->add($silverStripeVersion);
0 ignored issues
show
Documentation Bug introduced by
The method CompatibleVersions does not exist on object<AddonVersion>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
313
				}
314
			} catch (Exception $e) {
315
				// An exception here shouldn't prevent further updates.
316
				Debug::log($addon->Name . "\t" . $addon->ID . "\t" . $e->getMessage());
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<Addon>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
317
			}
318
		}
319
	}
320
321
	private function updateAuthors(AddonVersion $version, CompletePackage $package) {
322
		if ($package->getAuthors()) foreach ($package->getAuthors() as $details) {
323
			$author = null;
324
325
			if (!$details->getName() && !$details->getEmail()) {
326
				continue;
327
			}
328
329
			if ($details->getEmail()) {
330
				$author = AddonAuthor::get()->filter('Email', $details->getEmail())->first();
331
			}
332
333
			if (!$author && $details->getHomepage()) {
334
				$author = AddonAuthor::get()
335
					->filter('Name', $details->getName())
336
					->filter('Homepage', $details->getHomepage())
337
					->first();
338
			}
339
340
			if (!$author && $details->getName()) {
341
				$author = AddonAuthor::get()
342
					->filter('Name', $details->getName())
343
					->filter('Versions.Addon.Name', $package->getName())
344
					->first();
345
			}
346
347
			if (!$author) {
348
				$author = new AddonAuthor();
349
			}
350
351
			if($details->getName()) $author->Name = $details->getName();
352
			if($details->getEmail()) $author->Email = $details->getEmail();
353
			if($details->getHomepage()) $author->Homepage = $details->getHomepage();
354
355
			//to-do not supported by API
356
			//if(isset($details['role'])) $author->Role = $details['role'];
357
358
			$version->Authors()->add($author->write());
0 ignored issues
show
Documentation Bug introduced by
The method Authors does not exist on object<AddonVersion>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
359
		}
360
	}
361
362
    /**
363
     * Get the list of SilverStripe versions
364
     *
365
     * @return DataList
366
     */
367
    public function getSilverStripeVersions()
368
    {
369
        return $this->silverstripes;
370
    }
371
372
    /**
373
     * Set the list of SilverStripeVersions
374
     *
375
     * @param  DataList $versions
376
     * @return $this
377
     */
378
    public function setSilverStripeVersions(DataList $versions)
379
    {
380
        $this->silverstripes = $versions;
0 ignored issues
show
Documentation Bug introduced by
It seems like $versions of type object<DataList> is incompatible with the declared type array<integer,object<SilverStripeVersion>> of property $silverstripes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
381
        return $this;
382
    }
383
}
384