Issues (22)

src/Log.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * @category Library
4
 * @license MIT http://opensource.org/licenses/MIT
5
 * @link https://github.com/emlynwest/changelog
6
 */
7
8
namespace ChangeLog;
9
10
use ArrayIterator;
11
use Countable;
12
use IteratorAggregate;
13
use Naneau\SemVer\Parser;
14
use Naneau\SemVer\Sort;
15
use Naneau\SemVer\Version;
0 ignored issues
show
This use statement conflicts with another class in this namespace, ChangeLog\Version. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
use Traversable;
17
18
/**
19
 * Represents a full change log.
20
 */
21
class Log implements IteratorAggregate, Countable
22
{
23
	const VERSION_MAJOR = 'major';
24
	const VERSION_MINOR = 'minor';
25
	const VERSION_PATCH = 'patch';
26
27
	/**
28
	 * @var string
29
	 */
30
	protected $description = '';
31
32
	/**
33
	 * @var Release[]
34
	 */
35
	protected $releases = [];
36
37
	/**
38
	 * @var string
39
	 */
40
	protected $title = '';
41
42
	/**
43
	 * Gets all the releases for the log.
44
	 *
45
	 * @return Release[]
46
	 */
47 3
	public function getReleases()
48
	{
49 3
		return $this->releases;
50
	}
51
52
	/**
53
	 * Gets the named release.
54
	 *
55
	 * @param string $name
56
	 *
57
	 * @return Release|null
58
	 */
59 9
	public function getRelease($name)
60
	{
61 9
		$key = strtolower($name);
62 9
		if ( ! $this->hasRelease($key))
63
		{
64 1
			return null;
65
		}
66
67 9
		return $this->releases[$key];
68
	}
69
70
	/**
71
	 * Adds a release to the Log.
72
	 * Can be used to replace existing releases too.
73
	 *
74
	 * @param Release $release
75
	 */
76 23
	public function addRelease(Release $release)
77
	{
78 23
		$name = strtolower($release->getName());
79 23
		$this->releases[$name] = $release;
80 23
		$this->sortReleases();
81
	}
82
83
	/**
84
	 * Removes a release from the Log.
85
	 *
86
	 * @param string $name
87
	 */
88 4
	public function removeRelease($name)
89
	{
90 4
		$key = strtolower($name);
91 4
		unset($this->releases[$key]);
92
	}
93
94
	/**
95
	 * Checks if the Log has the named Release.
96
	 *
97
	 * @param string $name
98
	 *
99
	 * @return bool
100
	 */
101 13
	public function hasRelease($name)
102
	{
103 13
		$key = strtolower($name);
104 13
		return isset($this->releases[$key]);
105
	}
106
107
	/**
108
	 * @return string
109
	 */
110 14
	public function getDescription()
111
	{
112 14
		return $this->description;
113
	}
114
115
	/**
116
	 * @param string $description
117
	 */
118 17
	public function setDescription($description)
119
	{
120 17
		$this->description = $description;
121
	}
122
123
	/**
124
	 * @return string
125
	 */
126 14
	public function getTitle()
127
	{
128 14
		return $this->title;
129
	}
130
131
	/**
132
	 * @param string $title
133
	 */
134 17
	public function setTitle($title)
135
	{
136 17
		$this->title = $title;
137
	}
138
139
	/**
140
	 * {@inheritdoc}
141
	 */
142 18
	public function getIterator()
143
	{
144 18
		return new ArrayIterator($this->releases);
145
	}
146
147
	/**
148
	 * {@inheritdoc}
149
	 */
150 1
	public function count()
151
	{
152 1
		return count($this->releases);
153
	}
154
155
	/**
156
	 * Sorts the releases inside this log in accordance with semantic versioning, latest release first.
157
	 */
158 23
	public function sortReleases()
159
	{
160
		// If there is an unreleased release pull that out and sort the rest
161 23
		$unreleased = null;
162 23
		if (isset($this->releases['unreleased']))
163
		{
164 10
			$unreleased = $this->releases['unreleased'];
165 10
			unset($this->releases['unreleased']);
166
		}
167
168 23
		$order = Sort::sort(array_keys($this->releases));
169 23
		$order = array_reverse($order);
170
171 23
		$newOrder = [];
172
		/** @var Version $version */
173 23
		foreach ($order as $version)
174
		{
175 23
			$index = $version->__toString();
176 23
			$newOrder[$index] = $this->releases[$index];
177
		}
178
179 23
		if ($unreleased !== null)
180
		{
181 10
			$newOrder = ['unreleased' => $unreleased] + $newOrder;
182
		}
183
184 23
		$this->releases = $newOrder;
185
	}
186
187
	/**
188
	 * Merges another Log's releases with this log.
189
	 *
190
	 * @param Log $log
191
	 */
192 2
	public function mergeLog(Log $log)
193
	{
194
		/** @var Release $release */
195 2
		foreach ($log as $release)
196
		{
197 2
			$name = $release->getName();
198 2
			if ($this->hasRelease($name))
199
			{
200
				// if it does exist then merge the changes
201 2
				$this->mergeRelease($log, $name);
202
			}
203
			else
204
			{
205
				// If the release does not exist add it
206 2
				$this->addRelease($release);
207
			}
208
		}
209
	}
210
211
	/**
212
	 * Combines all changes of the name of the given release from the given log into this log.
213
	 *
214
	 * @param Log    $log
215
	 * @param string $name
216
	 */
217 2
	protected function mergeRelease(Log $log, $name)
218
	{
219 2
		$myRelease = $this->getRelease($name);
220 2
		$theirRelease = $log->getRelease($name);
221
222 2
		$changes = $this->mergeChangesArrays(
223 2
			$theirRelease->getAllChanges(),
224 2
			$myRelease->getAllChanges()
225 2
		);
226 2
		$myRelease->setAllChanges($changes);
227
	}
228
229
	/**
230
	 * Merges two sets of changes.
231
	 *
232
	 * @param array $left
233
	 * @param array $right
234
	 *
235
	 * @return array
236
	 */
237 2
	protected function mergeChangesArrays($left, $right)
238
	{
239 2
		$return = $left;
240
241 2
		foreach ($right as $type => $changes)
242
		{
243 2
			if (isset($left[$type]))
244
			{
245 2
				$return[$type] = array_merge($right[$type], $left[$type]);
246
			}
247
			else
248
			{
249 1
				$return[$type] = $changes;
250
			}
251
		}
252
253 2
		return $return;
254
	}
255
256
	/**
257
	 * @return Release
258
	 */
259 2
	public function getLatestRelease()
260
	{
261 2
		$releases = $this->releases;
262
263 2
		$release = array_shift($releases);
264
265 2
		if (count($this->releases) > 1 && strtolower($release->getName()) === 'unreleased')
266
		{
267 1
			$release = array_shift($releases);
268
		}
269
270 2
		return $release;
271
	}
272
273 5
	public function getNextVersion($type)
274
	{
275 5
		if (! in_array($type, [static::VERSION_MAJOR, static::VERSION_MINOR, static::VERSION_PATCH]))
276
		{
277 5
			return $type;
278
		}
279
280 1
		$latestRelease = $this->getLatestRelease();
281
282 1
		$version = $latestRelease->getName() === 'unreleased' ? '0.0.0' : $latestRelease->getName() ;
283
284 1
		$semver = Parser::parse($version);
285 1
		$patch = $semver->getPatch();
286 1
		$minor = $semver->getMinor();
287 1
		$major = $semver->getMajor();
288
289
		switch ($type)
290
		{
291
			case Log::VERSION_PATCH:
292 1
				$patch++;
293 1
				break;
294
			case Log::VERSION_MINOR:
295 1
				$minor++;
296 1
				break;
297
			case Log::VERSION_MAJOR:
298 1
				$major++;
299 1
				break;
300
		}
301
302 1
		return "$major.$minor.$patch";
303
	}
304
305
}
306