├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── res
└── schema.json
└── src
├── lib
├── Herrera
│ └── Phar
│ │ └── Update
│ │ ├── Exception
│ │ ├── Exception.php
│ │ ├── ExceptionInterface.php
│ │ ├── FileException.php
│ │ ├── InvalidArgumentException.php
│ │ └── LogicException.php
│ │ ├── Manager.php
│ │ ├── Manifest.php
│ │ └── Update.php
└── constants.php
└── tests
├── Herrera
└── Phar
│ └── Update
│ ├── Exception
│ └── ExceptionTest.php
│ ├── ManagerTest.php
│ ├── ManifestTest.php
│ └── UpdateTest.php
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /bin/
3 | /coverage/
4 | /src/vendors/
5 |
6 | /*.iml
7 | /composer.lock
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.3
5 | - 5.4
6 |
7 | before_script:
8 | - composer self-update
9 | - composer install --dev
10 |
11 | script: bin/phpunit
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Kevin Herrera
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Phar Update
2 | ===========
3 |
4 | [](https://travis-ci.org/herrera-io/php-phar-update)
5 |
6 | A library for self-updating Phars.
7 |
8 | Summary
9 | -------
10 |
11 | This library handles the updating of applications packaged as distributable Phars. The modular design allows for a more customizable update process.
12 |
13 | Installation
14 | ------------
15 |
16 | Add it to your list of Composer dependencies:
17 |
18 | ```sh
19 | $ composer require herrera-io/phar-update=1.*
20 | ```
21 |
22 | Usage
23 | -----
24 |
25 | ```php
26 | update('1.0.0', true);
37 | ```
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "herrera-io/phar-update",
3 | "description": "A library for self-updating Phars.",
4 | "keywords": ["phar", "update"],
5 | "homepage": "http://herrera-io.github.com/php-phar-update",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Kevin Herrera",
10 | "email": "kevin@herrera.io",
11 | "homepage": "http://kevin.herrera.io"
12 | }
13 | ],
14 | "support": {
15 | "issues": "https://github.com/herrera-io/php-phar-update/issues"
16 | },
17 | "require": {
18 | "php": ">=5.3.3",
19 | "herrera-io/json": "1.*",
20 | "herrera-io/version": "1.*"
21 | },
22 | "require-dev": {
23 | "herrera-io/phpunit-test-case": "1.*",
24 | "mikey179/vfsStream": "1.1.0",
25 | "phpunit/phpunit": "3.7.*"
26 | },
27 | "autoload": {
28 | "files": ["src/lib/constants.php"],
29 | "psr-0": {
30 | "Herrera\\Phar\\Update": "src/lib"
31 | }
32 | },
33 | "config": {
34 | "bin-dir": "bin",
35 | "vendor-dir": "src/vendors"
36 | },
37 | "extra": {
38 | "branch-alias": {
39 | "dev-master": "2.0-dev"
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | src/lib/
15 |
16 |
17 |
18 |
19 | src/tests/
20 |
21 |
22 |
--------------------------------------------------------------------------------
/res/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Update Manifest Schema",
3 | "type": "array",
4 | "items": {
5 | "title": "Update",
6 | "type": "object",
7 | "additionalProperties": false,
8 | "properties": {
9 | "name": {
10 | "description": "The name of the file that will be downloaded.",
11 | "type": "string",
12 | "pattern": "\\.phar$"
13 | },
14 | "publicKey": {
15 | "description": "The location of the public key file.",
16 | "type": "string"
17 | },
18 | "sha1": {
19 | "description": "The SHA1 file checksum.",
20 | "type": "string",
21 | "pattern": "^[a-f0-9]{40}$"
22 | },
23 | "url": {
24 | "description": "The location of the update file.",
25 | "type": "string"
26 | },
27 | "version": {
28 | "description": "The semantic version number.",
29 | "type": "string",
30 | "pattern": "^\\d+\\.\\d+\\.\\d+(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?$"
31 | }
32 | },
33 | "required": ["name", "sha1", "url", "version"]
34 | }
35 | }
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Exception/Exception.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Exception extends \Exception implements ExceptionInterface
11 | {
12 | /**
13 | * Creates a new exception using a format and values.
14 | *
15 | * @param string $format The format.
16 | * @param mixed $value,... The value(s).
17 | *
18 | * @return Exception The exception.
19 | */
20 | public static function create($format, $value = null)
21 | {
22 | if (0 < func_num_args()) {
23 | $format = vsprintf($format, array_slice(func_get_args(), 1));
24 | }
25 |
26 | return new static($format);
27 | }
28 |
29 | /**
30 | * Creates an exception for the last error message.
31 | *
32 | * @return Exception The exception.
33 | */
34 | public static function lastError()
35 | {
36 | $error = error_get_last();
37 |
38 | return new static($error['message']);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Exception/ExceptionInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | interface ExceptionInterface
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Exception/FileException.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class FileException extends Exception
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Exception/InvalidArgumentException.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class InvalidArgumentException extends Exception
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Exception/LogicException.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class LogicException extends Exception
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Manager.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Manager
15 | {
16 | /**
17 | * The update manifest.
18 | *
19 | * @var Manifest
20 | */
21 | private $manifest;
22 |
23 | /**
24 | * The running file (the Phar that will be updated).
25 | *
26 | * @var string
27 | */
28 | private $runningFile;
29 |
30 | /**
31 | * Sets the update manifest.
32 | *
33 | * @param Manifest $manifest The manifest.
34 | */
35 | public function __construct(Manifest $manifest)
36 | {
37 | $this->manifest = $manifest;
38 | }
39 |
40 | /**
41 | * Returns the manifest.
42 | *
43 | * @return Manifest The manifest.
44 | */
45 | public function getManifest()
46 | {
47 | return $this->manifest;
48 | }
49 |
50 | /**
51 | * Returns the running file (the Phar that will be updated).
52 | *
53 | * @return string The file.
54 | */
55 | public function getRunningFile()
56 | {
57 | if (null === $this->runningFile) {
58 | $this->runningFile = realpath($_SERVER['argv'][0]);
59 | }
60 |
61 | return $this->runningFile;
62 | }
63 |
64 | /**
65 | * Sets the running file (the Phar that will be updated).
66 | *
67 | * @param string $file The file name or path.
68 | *
69 | * @throws Exception\Exception
70 | * @throws InvalidArgumentException If the file path is invalid.
71 | */
72 | public function setRunningFile($file)
73 | {
74 | if (false === is_file($file)) {
75 | throw InvalidArgumentException::create(
76 | 'The file "%s" is not a file or it does not exist.',
77 | $file
78 | );
79 | }
80 |
81 | $this->runningFile = $file;
82 | }
83 |
84 | /**
85 | * Updates the running Phar if any is available.
86 | *
87 | * @param string|Version $version The current version.
88 | * @param boolean $major Lock to current major version?
89 | * @param boolean $pre Allow pre-releases?
90 | *
91 | * @return boolean TRUE if an update was performed, FALSE if none available.
92 | */
93 | public function update($version, $major = false, $pre = false)
94 | {
95 | if (false === ($version instanceof Version)) {
96 | $version = Parser::toVersion($version);
97 | }
98 |
99 | if (null !== ($update = $this->manifest->findRecent(
100 | $version,
101 | $major,
102 | $pre
103 | ))) {
104 | $update->getFile();
105 | $update->copyTo($this->getRunningFile());
106 |
107 | return true;
108 | }
109 |
110 | return false;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Manifest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Manifest
16 | {
17 | /**
18 | * The list of updates in the manifest.
19 | *
20 | * @var Update[]
21 | */
22 | private $updates;
23 |
24 | /**
25 | * Sets the list of updates from the manifest.
26 | *
27 | * @param Update[] $updates The updates.
28 | */
29 | public function __construct(array $updates = array())
30 | {
31 | $this->updates = $updates;
32 | }
33 |
34 | /**
35 | * Finds the most recent update and returns it.
36 | *
37 | * @param Version $version The current version.
38 | * @param boolean $major Lock to major version?
39 | * @param boolean $pre Allow pre-releases?
40 | *
41 | * @return Update The update.
42 | */
43 | public function findRecent(Version $version, $major = false, $pre = false)
44 | {
45 | /** @var $current Update */
46 | $current = null;
47 | $major = $major ? $version->getMajor() : null;
48 |
49 | foreach ($this->updates as $update) {
50 | if ($major && ($major !== $update->getVersion()->getMajor())) {
51 | continue;
52 | }
53 |
54 | if ((false === $pre)
55 | && !$update->getVersion()->isStable()) {
56 | continue;
57 | }
58 |
59 | $test = $current ? $current->getVersion() : $version;
60 |
61 | if (false === $update->isNewer($test)) {
62 | continue;
63 | }
64 |
65 | $current = $update;
66 | }
67 |
68 | return $current;
69 | }
70 |
71 | /**
72 | * Returns the list of updates in the manifest.
73 | *
74 | * @return Update[] The updates.
75 | */
76 | public function getUpdates()
77 | {
78 | return $this->updates;
79 | }
80 |
81 | /**
82 | * Loads the manifest from a JSON encoded string.
83 | *
84 | * @param string $json The JSON encoded string.
85 | *
86 | * @return Manifest The manifest.
87 | */
88 | public static function load($json)
89 | {
90 | $j = new Json();
91 |
92 | return self::create($j->decode($json), $j);
93 | }
94 |
95 | /**
96 | * Loads the manifest from a JSON encoded file.
97 | *
98 | * @param string $file The JSON encoded file.
99 | *
100 | * @return Manifest The manifest.
101 | */
102 | public static function loadFile($file)
103 | {
104 | $json = new Json();
105 |
106 | return self::create($json->decodeFile($file), $json);
107 | }
108 |
109 | /**
110 | * Validates the data, processes it, and returns a new instance of Manifest.
111 | *
112 | * @param array $decoded The decoded JSON data.
113 | * @param Json $json The Json instance used to decode the data.
114 | *
115 | * @return Manifest The new instance.
116 | */
117 | private static function create($decoded, Json $json)
118 | {
119 | $json->validate(
120 | $json->decodeFile(PHAR_UPDATE_MANIFEST_SCHEMA),
121 | $decoded
122 | );
123 |
124 | $updates = array();
125 |
126 | foreach ($decoded as $update) {
127 | $updates[] = new Update(
128 | $update->name,
129 | $update->sha1,
130 | $update->url,
131 | Parser::toVersion($update->version),
132 | isset($update->publicKey) ? $update->publicKey : null
133 | );
134 | }
135 |
136 | usort(
137 | $updates,
138 | function (Update $a, Update $b) {
139 | return Comparator::isGreaterThan(
140 | $a->getVersion(),
141 | $b->getVersion()
142 | );
143 | }
144 | );
145 |
146 | return new static($updates);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/lib/Herrera/Phar/Update/Update.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Update
19 | {
20 | /**
21 | * The temporary file path.
22 | *
23 | * @var string
24 | */
25 | private $file;
26 |
27 | /**
28 | * The name of the update file.
29 | *
30 | * @var string
31 | */
32 | private $name;
33 |
34 | /**
35 | * The URL where the public key can be downloaded from.
36 | *
37 | * @var string
38 | */
39 | private $publicKey;
40 |
41 | /**
42 | * The SHA1 file checksum.
43 | *
44 | * @var string
45 | */
46 | private $sha1;
47 |
48 | /**
49 | * The URL where the update can be downloaded from.
50 | *
51 | * @var string
52 | */
53 | private $url;
54 |
55 | /**
56 | * The version of the update.
57 | *
58 | * @var Version
59 | */
60 | private $version;
61 |
62 | /**
63 | * Sets the update information.
64 | *
65 | * @param string $name The name of the update file.
66 | * @param string $sha1 The SHA1 file checksum.
67 | * @param string $url The URL where the update can be downloaded from.
68 | * @param Version $version The version of the update.
69 | * @param string $key The URL where the public key can be downloaded
70 | * from.
71 | */
72 | public function __construct(
73 | $name,
74 | $sha1,
75 | $url,
76 | Version $version,
77 | $key = null
78 | ) {
79 | $this->name = $name;
80 | $this->publicKey = $key;
81 | $this->sha1 = $sha1;
82 | $this->url = $url;
83 | $this->version = $version;
84 | }
85 |
86 | /**
87 | * Copies the update file to the destination.
88 | *
89 | * @param string $file The target file.
90 | *
91 | * @throws Exception\Exception
92 | * @throws FileException If the file could not be replaced.
93 | */
94 | public function copyTo($file)
95 | {
96 | if (null === $this->file) {
97 | throw LogicException::create(
98 | 'The update file has not been downloaded.'
99 | );
100 | }
101 |
102 | $mode = 0755;
103 |
104 | if (file_exists($file)) {
105 | $mode = fileperms($file) & 511;
106 | }
107 |
108 | if (false === @copy($this->file, $file)) {
109 | throw FileException::lastError();
110 | }
111 |
112 | if (false === @chmod($file, $mode)) {
113 | throw FileException::lastError();
114 | }
115 |
116 | $key = $file . '.pubkey';
117 |
118 | if (file_exists($this->file . '.pubkey')) {
119 | if (false === @copy($this->file . '.pubkey', $key)) {
120 | throw FileException::lastError();
121 | }
122 | } elseif (file_exists($key)) {
123 | if (false === @unlink($key)) {
124 | throw FileException::lastError();
125 | }
126 | }
127 | }
128 |
129 | /**
130 | * Cleans up by deleting the temporary update file.
131 | *
132 | * @throws FileException If the file could not be deleted.
133 | */
134 | public function deleteFile()
135 | {
136 | if ($this->file) {
137 | if (file_exists($this->file)) {
138 | if (false === @unlink($this->file)) {
139 | throw FileException::lastError();
140 | }
141 | }
142 |
143 | if (file_exists($this->file . '.pubkey')) {
144 | if (false === @unlink($this->file . '.pubkey')) {
145 | throw FileException::lastError();
146 | }
147 | }
148 |
149 | $dir = dirname($this->file);
150 |
151 | if (file_exists($dir)) {
152 | if (false === @rmdir($dir)) {
153 | throw FileException::lastError();
154 | }
155 | }
156 |
157 | $this->file = null;
158 | }
159 | }
160 |
161 | /**
162 | * Downloads the update file to a temporary location.
163 | *
164 | * @return string The temporary file path.
165 | *
166 | * @throws Exception\Exception
167 | * @throws FileException If the SHA1 checksum differs.
168 | * @throws UnexpectedValueException If the Phar is corrupt.
169 | */
170 | public function getFile()
171 | {
172 | if (null === $this->file) {
173 | unlink($this->file = tempnam(sys_get_temp_dir(), 'upd'));
174 | mkdir($this->file);
175 |
176 | $this->file .= DIRECTORY_SEPARATOR . $this->name;
177 |
178 | $in = new SplFileObject($this->url, 'rb', false);
179 | $out = new SplFileObject($this->file, 'wb', false);
180 |
181 | while (false === $in->eof()) {
182 | $out->fwrite($in->fgets());
183 | }
184 |
185 | unset($in, $out);
186 |
187 | if ($this->publicKey) {
188 | $in = new SplFileObject($this->publicKey, 'r', false);
189 | $out = new SplFileObject($this->file . '.pubkey', 'w', false);
190 |
191 | while (false === $in->eof()) {
192 | $out->fwrite($in->fgets());
193 | }
194 |
195 | unset($in, $out);
196 | }
197 |
198 | if ($this->sha1 !== ($sha1 = sha1_file($this->file))) {
199 | $this->deleteFile();
200 |
201 | throw FileException::create(
202 | 'Mismatch of the SHA1 checksum (%s) of the downloaded file (%s).',
203 | $this->sha1,
204 | $sha1
205 | );
206 | }
207 |
208 | // double check
209 | try {
210 | new Phar($this->file);
211 | } catch (UnexpectedValueException $exception) {
212 | $this->deleteFile();
213 |
214 | throw $exception;
215 | }
216 | }
217 |
218 | return $this->file;
219 | }
220 |
221 | /**
222 | * Returns name of the update file.
223 | *
224 | * @return string The name.
225 | */
226 | public function getName()
227 | {
228 | return $this->name;
229 | }
230 |
231 | /**
232 | * Returns the URL where the public key can be downloaded from.
233 | *
234 | * @return string The URL.
235 | */
236 | public function getPublicKey()
237 | {
238 | return $this->publicKey;
239 | }
240 |
241 | /**
242 | * Returns the SHA1 file checksum.
243 | *
244 | * @return string The checksum.
245 | */
246 | public function getSha1()
247 | {
248 | return $this->sha1;
249 | }
250 |
251 | /**
252 | * Returns the URL where the update can be downloaded from.
253 | *
254 | * @return string The URL.
255 | */
256 | public function getUrl()
257 | {
258 | return $this->url;
259 | }
260 |
261 | /**
262 | * Returns the version of the update.
263 | *
264 | * @return Version The version.
265 | */
266 | public function getVersion()
267 | {
268 | return $this->version;
269 | }
270 |
271 | /**
272 | * Checks if this update is newer than the version given.
273 | *
274 | * @param Version $version The current version.
275 | *
276 | * @return boolean TRUE if the update is newer, FALSE if not.
277 | */
278 | public function isNewer(Version $version)
279 | {
280 | return Comparator::isGreaterThan($this->version, $version);
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/src/lib/constants.php:
--------------------------------------------------------------------------------
1 | assertEquals('My message.', $exception->getMessage());
15 | }
16 |
17 | public function testLastError()
18 | {
19 | @$test;
20 |
21 | $exception = Exception::lastError();
22 |
23 | $this->assertEquals('Undefined variable: test', $exception->getMessage());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/tests/Herrera/Phar/Update/ManagerTest.php:
--------------------------------------------------------------------------------
1 | assertSame($this->manifest, $this->manager->getManifest());
22 | }
23 |
24 | public function testGetRunningFile()
25 | {
26 | $this->assertEquals(
27 | realpath($_SERVER['argv'][0]),
28 | $this->manager->getRunningFile()
29 | );
30 | }
31 |
32 | /**
33 | * @depends testGetRunningFile
34 | */
35 | public function testSetRunningFile()
36 | {
37 | $file = $this->createFile();
38 |
39 | $this->manager->setRunningFile($file);
40 |
41 | $this->assertEquals($file, $this->manager->getRunningFile());
42 | }
43 |
44 | public function testSetRunningFileNotExist()
45 | {
46 | $this->setExpectedException(
47 | 'Herrera\\Phar\\Update\\Exception\\InvalidArgumentException',
48 | 'The file "/does/not/exist" is not a file or it does not exist.'
49 | );
50 |
51 | $this->manager->setRunningFile('/does/not/exist');
52 | }
53 |
54 | /**
55 | * @depends testSetRunningFile
56 | */
57 | public function testUpdate()
58 | {
59 | unlink($currentFile = $this->createFile('current.phar'));
60 | unlink($newFile = $this->createFile('new.phar'));
61 |
62 | $current = new Phar($currentFile);
63 | $current->addFromString('test.php', 'setStub($current->createDefaultStub('test.php'));
65 |
66 | $new = new Phar($newFile);
67 | $new->addFromString('test.php', 'setStub($new->createDefaultStub('test.php'));
69 |
70 | unset($current, $new);
71 |
72 | $manager = new Manager(new Manifest(array(new Update(
73 | 'new.phar',
74 | sha1_file($newFile),
75 | $newFile,
76 | Parser::toVersion('1.0.1')
77 | ))));
78 |
79 | $manager->setRunningFile($currentFile);
80 |
81 | $this->assertTrue($manager->update('1.0.0'));
82 | $this->assertEquals('new', exec('php ' . escapeshellarg($currentFile)));
83 | }
84 |
85 | public function testUpdateNone()
86 | {
87 | $manager = new Manager(new Manifest(array(new Update(
88 | 'new.phar',
89 | 'test',
90 | 'test',
91 | Parser::toVersion('2.0.1')
92 | ))));
93 |
94 | $manager->setRunningFile($this->createFile());
95 |
96 | $this->assertFalse($manager->update('1.0.0', true));
97 | }
98 |
99 | protected function setUp()
100 | {
101 | $this->manifest = new Manifest();
102 | $this->manager = new Manager($this->manifest);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/tests/Herrera/Phar/Update/ManifestTest.php:
--------------------------------------------------------------------------------
1 | assertSame(
30 | $this->v1,
31 | $this->manifest->findRecent($version, true)
32 | );
33 | $this->assertSame(
34 | $this->v1p,
35 | $this->manifest->findRecent(
36 | Parser::toVersion('2.0.0-alpha.1'),
37 | true,
38 | true
39 | )
40 | );
41 | $this->assertSame($this->v2, $this->manifest->findRecent($version));
42 | }
43 |
44 | public function testFindRecentNone()
45 | {
46 | $this->assertNull(
47 | $this->manifest->findRecent(Parser::toVersion('5.0.0'))
48 | );
49 | }
50 |
51 | public function testGetUpdates()
52 | {
53 | $this->assertSame(
54 | array($this->v1, $this->v1p, $this->v2),
55 | $this->manifest->getUpdates()
56 | );
57 | }
58 |
59 | public function testLoad()
60 | {
61 | $data = json_encode(array(
62 | array(
63 | 'name' => 'test.phar',
64 | 'publicKey' => 'http://example.com/test-1.2.3.phar.pubkey',
65 | 'sha1' => 'abcdefabcdefabcdefabcdefabcdefabcdefabcd',
66 | 'url' => 'http://example.com/test-1.2.3.phar',
67 | 'version' => '1.2.3',
68 | ),
69 | array(
70 | 'name' => 'test.phar',
71 | 'publicKey' => 'http://example.com/test-4.5.6.phar.pubkey',
72 | 'sha1' => '0123456789012345678901234567890123456789',
73 | 'url' => 'http://example.com/test-4.5.6.phar',
74 | 'version' => '4.5.6'
75 | )
76 | ));
77 |
78 | try {
79 | $updates = Manifest::load($data)->getUpdates();
80 | } catch (JsonException $exception) {
81 | print_r($exception->getErrors());
82 | throw $exception;
83 | }
84 |
85 | $this->assertEquals(
86 | 'http://example.com/test-1.2.3.phar.pubkey',
87 | $updates[0]->getPublicKey()
88 | );
89 | $this->assertEquals(
90 | 'http://example.com/test-4.5.6.phar.pubkey',
91 | $updates[1]->getPublicKey()
92 | );
93 | }
94 |
95 | public function testLoadFile()
96 | {
97 | file_put_contents($file = $this->createFile(), json_encode(array(
98 | array(
99 | 'name' => 'test.phar',
100 | 'sha1' => 'abcdefabcdefabcdefabcdefabcdefabcdefabcd',
101 | 'url' => 'http://example.com/test-1.2.3.phar',
102 | 'version' => '1.2.3'
103 | ),
104 | array(
105 | 'name' => 'test.phar',
106 | 'sha1' => '0123456789012345678901234567890123456789',
107 | 'url' => 'http://example.com/test-4.5.6.phar',
108 | 'version' => '4.5.6'
109 | )
110 | )));
111 |
112 | try {
113 | $updates = Manifest::loadFile($file)->getUpdates();
114 | } catch (JsonException $exception) {
115 | print_r($exception->getErrors());
116 | throw $exception;
117 | }
118 |
119 | $this->assertEquals(
120 | 'abcdefabcdefabcdefabcdefabcdefabcdefabcd',
121 | $updates[0]->getSha1()
122 | );
123 | $this->assertEquals(
124 | '0123456789012345678901234567890123456789',
125 | $updates[1]->getSha1()
126 | );
127 | }
128 |
129 | protected function setUp()
130 | {
131 | $this->v1 = new Update(
132 | 'test.phar',
133 | '0123456789012345678901234567890123456789',
134 | 'http://example.com/test.phar',
135 | Parser::toVersion('1.2.3')
136 | );
137 |
138 | $this->v1p = new Update(
139 | 'test.phar',
140 | '0123456789012345678901234567890123456789',
141 | 'http://example.com/test.phar',
142 | Parser::toVersion('2.0.0-alpha.2')
143 | );
144 |
145 | $this->v2 = new Update(
146 | 'test.phar',
147 | '0123456789012345678901234567890123456789',
148 | 'http://example.com/test.phar',
149 | Parser::toVersion('4.5.6')
150 | );
151 |
152 | $this->manifest = new Manifest(array(
153 | $this->v1,
154 | $this->v1p,
155 | $this->v2
156 | ));
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/tests/Herrera/Phar/Update/UpdateTest.php:
--------------------------------------------------------------------------------
1 | createFile('test.phar'));
24 |
25 | $key = $this->getKey();
26 | $phar = new Phar($file);
27 | $phar->addFromString('test.php', 'setStub($phar->createDefaultStub('test.php'));
29 | $phar->setSignatureAlgorithm(
30 | Phar::OPENSSL,
31 | $key[0]
32 | );
33 |
34 | file_put_contents($file . '.pubkey', $key[1]);
35 |
36 | unset($phar);
37 |
38 | $this->setPropertyValue($this->update, 'file', $file);
39 |
40 | return $file;
41 | }
42 |
43 | public function getKey()
44 | {
45 | return array(
46 | <<createPhar();
94 | $target = $this->createFile('taco.phar');
95 |
96 | chmod($file, 0755);
97 |
98 | $this->update->copyTo($target);
99 |
100 | $this->assertFileEquals($file, $target);
101 | $this->assertFileEquals($file . '.pubkey', $target . '.pubkey');
102 |
103 | if (false === strpos(strtolower(PHP_OS), 'win')) {
104 | $this->assertEquals(0755, fileperms($file) & 511);
105 | }
106 | }
107 |
108 | public function testCopyToNotDownloaded()
109 | {
110 | $this->setExpectedException(
111 | 'Herrera\\Phar\\Update\\Exception\\LogicException',
112 | 'The update file has not been downloaded.'
113 | );
114 |
115 | $this->update->copyTo($this->createFile());
116 | }
117 |
118 | public function testCopyToCopyError()
119 | {
120 | $this->createPhar();
121 |
122 | $root = vfsStream::newDirectory('test');
123 | $root->addChild(vfsStream::newFile('test.phar', 0000));
124 |
125 | $this->setExpectedException(
126 | 'Herrera\\Phar\\Update\\Exception\\FileException',
127 | 'failed to open stream'
128 | );
129 |
130 | $this->update->copyTo('vfs://test/test.phar');
131 | }
132 |
133 | public function testDeleteFile()
134 | {
135 | $file = $this->createFile('test.phar');
136 |
137 | $this->setPropertyValue($this->update, 'file', $file);
138 |
139 | $this->update->deleteFile();
140 |
141 | $this->assertFileNotExists(dirname($file));
142 | }
143 |
144 | public function testDeleteFileUnlinkError()
145 | {
146 | $root = vfsStream::newDirectory('test');
147 | $root->addChild(vfsStream::newFile('test.phar', 0000));
148 |
149 | vfsStreamWrapper::setRoot($root);
150 |
151 | $this->setPropertyValue($this->update, 'file', 'vfs://test/test.phar');
152 |
153 | // unlink() does not issue warning on streams, but does return false
154 | $this->setExpectedException(
155 | 'Herrera\\Phar\\Update\\Exception\\FileException'
156 | );
157 |
158 | $this->update->deleteFile();
159 | }
160 |
161 | public function testDeleteFileRmdirError()
162 | {
163 | $file = $this->createFile();
164 |
165 | $this->setPropertyValue(
166 | $this->update,
167 | 'file',
168 | $file . DIRECTORY_SEPARATOR . 'test.phar'
169 | );
170 |
171 | $this->setExpectedException(
172 | 'Herrera\\Phar\\Update\\Exception\\FileException',
173 | 'rmdir'
174 | );
175 |
176 | $this->update->deleteFile();
177 | }
178 |
179 | public function testGetFile()
180 | {
181 | unlink($file = $this->createFile('test.phar'));
182 |
183 | $key = $this->getKey();
184 | $phar = new Phar($file);
185 | $phar->addFromString('test.php', 'setStub($phar->createDefaultStub('test.php'));
187 | $phar->setSignatureAlgorithm(
188 | Phar::OPENSSL,
189 | $key[0]
190 | );
191 |
192 | file_put_contents($file . '.pubkey', $key[1]);
193 |
194 | unset($phar);
195 |
196 | $this->setPropertyValue($this->update, 'publicKey', $file . '.pubkey');
197 | $this->setPropertyValue($this->update, 'sha1', sha1_file($file));
198 | $this->setPropertyValue($this->update, 'url', $file);
199 |
200 | $this->assertFileEquals($file, $this->update->getFile());
201 | }
202 |
203 | public function testGetFileCorrupt()
204 | {
205 | $file = $this->createFile('test.phar');
206 |
207 | file_put_contents($file, 'setPropertyValue($this->update, 'publicKey', null);
210 | $this->setPropertyValue($this->update, 'sha1', sha1_file($file));
211 | $this->setPropertyValue($this->update, 'url', $file);
212 |
213 | $this->setExpectedException('UnexpectedValueException');
214 |
215 | $this->assertFileEquals($file, $this->update->getFile());
216 | }
217 |
218 | public function testGetFileSha1Mismatch()
219 | {
220 | $file = $this->createFile();
221 |
222 | file_put_contents($file, 'test');
223 |
224 | $this->setPropertyValue($this->update, 'publicKey', null);
225 | $this->setPropertyValue($this->update, 'url', $file);
226 |
227 | $this->setExpectedException(
228 | 'Herrera\\Phar\\Update\\Exception\\FileException',
229 | 'Mismatch of the SHA1 checksum (1234567890123456789012345678901234567890) of the downloaded file (' . sha1_file($file) . ').'
230 | );
231 |
232 | $this->update->getFile();
233 | }
234 |
235 | public function testGetName()
236 | {
237 | $this->assertEquals('test.phar', $this->update->getName());
238 | }
239 |
240 | public function testGetPublicKey()
241 | {
242 | $this->assertEquals(
243 | 'http://example.com/test-1.2.3.phar.pubkey',
244 | $this->update->getPublicKey()
245 | );
246 | }
247 |
248 | public function testGetSha1()
249 | {
250 | $this->assertEquals(
251 | '1234567890123456789012345678901234567890',
252 | $this->update->getSha1()
253 | );
254 | }
255 |
256 | public function testGetUrl()
257 | {
258 | $this->assertEquals(
259 | 'http://example.com/test.phar',
260 | $this->update->getUrl()
261 | );
262 | }
263 |
264 | public function testGetVersion()
265 | {
266 | $this->assertSame($this->version, $this->update->getVersion());
267 | }
268 |
269 | public function testIsNewer()
270 | {
271 | $this->assertTrue($this->update->isNewer(Parser::toVersion('1.0.0')));
272 | }
273 |
274 | protected function setUp()
275 | {
276 | $this->update = new Update(
277 | 'test.phar',
278 | '1234567890123456789012345678901234567890',
279 | 'http://example.com/test.phar',
280 | $this->version = Parser::toVersion('1.2.3'),
281 | 'http://example.com/test-1.2.3.phar.pubkey'
282 | );
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/src/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |