diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php new file mode 100644 index 000000000..3d069af80 --- /dev/null +++ b/src/Composer/Package/Locker.php @@ -0,0 +1,102 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +use Composer\Json\JsonFile; +use Composer\Repository\RepositoryManager; + +/** + * Reads/writes project lockfile (composer.lock). + * + * @author Konstantin Kudryashiv + */ +class Locker +{ + private $lockFile; + private $repositoryManager; + + /** + * Initializes packages locker. + * + * @param JsonFile $lockFile lockfile loader + * @param RepositoryManager $repositoryManager repository manager instance + */ + public function __construct(JsonFile $lockFile, RepositoryManager $repositoryManager) + { + $this->lockFile = $lockFile; + $this->repositoryManager = $repositoryManager; + } + + /** + * Checks whether locker were been locked (lockfile found). + * + * @return Boolean + */ + public function isLocked() + { + return $this->lockFile->exists(); + } + + /** + * Searches and returns an array of locked packages, retrieved from registered repositories. + * + * @return array + */ + public function getLockedPackages() + { + if (!$this->isLocked()) { + throw new \LogicException('No lockfile found. Unable to read locked packages'); + } + + $lockList = $this->lockFile->read(); + $packages = array(); + foreach ($lockList as $info) { + $package = $this->repositoryManager->findPackage($info['package'], $info['version']); + + if (!$package) { + throw new \LogicException(sprintf( + 'Can not find "%s-%s" package in registered repositories', + $info['package'], $info['version'] + )); + } + + $packages[] = $package; + } + + return $packages; + } + + /** + * Locks provided packages into lockfile. + * + * @param array $packages array of packages + */ + public function lockPackages(array $packages) + { + $hash = array(); + foreach ($packages as $package) { + $name = $package->getName(); + $version = $package->getVersion(); + + if (!$name || !$version) { + throw new \LogicException(sprintf( + 'Package "%s" has no version or name and can not be locked', $package + )); + } + + $hash[] = array('package' => $name, 'version' => $version); + } + + $this->lockFile->write($hash); + } +} diff --git a/src/Composer/Package/PackageLock.php b/src/Composer/Package/PackageLock.php deleted file mode 100644 index e30ee3605..000000000 --- a/src/Composer/Package/PackageLock.php +++ /dev/null @@ -1,91 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package; - -use Composer\Package\MemoryPackage; -use Composer\Package\Version\VersionParser; - -/** - * @author Konstantin Kudryashiv - */ -class PackageLock -{ - private $file; - private $isLocked = false; - - public function __construct($file = 'composer.lock') - { - if (file_exists($file)) { - $this->file = $file; - $this->isLocked = true; - } - } - - public function isLocked() - { - return $this->isLocked; - } - - public function getLockedPackages() - { - $lockList = $this->loadJsonConfig($this->file); - - $versionParser = new VersionParser(); - $packages = array(); - foreach ($lockList as $info) { - $version = $versionParser->normalize($info['version']); - $packages[] = new MemoryPackage($info['package'], $version); - } - - return $packages; - } - - public function lock(array $packages) - { - // TODO: write installed packages info into $this->file - } - - private function loadJsonConfig($json) - { - if (is_file($json)) { - $json = file_get_contents($json); - } - - $config = json_decode($json, true); - if (!$config) { - switch (json_last_error()) { - case JSON_ERROR_NONE: - $msg = 'No error has occurred, is your composer.json file empty?'; - break; - case JSON_ERROR_DEPTH: - $msg = 'The maximum stack depth has been exceeded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $msg = 'Invalid or malformed JSON'; - break; - case JSON_ERROR_CTRL_CHAR: - $msg = 'Control character error, possibly incorrectly encoded'; - break; - case JSON_ERROR_SYNTAX: - $msg = 'Syntax error'; - break; - case JSON_ERROR_UTF8: - $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - } - throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg); - } - - return $config; - } -} diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php new file mode 100644 index 000000000..49482d9d4 --- /dev/null +++ b/tests/Composer/Test/Package/LockerTest.php @@ -0,0 +1,189 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Package; + +use Composer\Package\Locker; + +class LockerTest extends \PHPUnit_Framework_TestCase +{ + public function testIsLocked() + { + $json = $this->createJsonFileMock(); + $locker = new Locker($json, $this->createRepositoryManagerMock()); + + $json + ->expects($this->once()) + ->method('exists') + ->will($this->returnValue(true)); + + $this->assertTrue($locker->isLocked()); + } + + public function testGetNotLockedPackages() + { + $json = $this->createJsonFileMock(); + $repo = $this->createRepositoryManagerMock(); + + $locker = new Locker($json, $repo); + + $json + ->expects($this->once()) + ->method('exists') + ->will($this->returnValue(false)); + + $this->setExpectedException('LogicException'); + + $locker->getLockedPackages(); + } + + public function testGetLockedPackages() + { + $json = $this->createJsonFileMock(); + $repo = $this->createRepositoryManagerMock(); + + $locker = new Locker($json, $repo); + + $json + ->expects($this->once()) + ->method('exists') + ->will($this->returnValue(true)); + $json + ->expects($this->once()) + ->method('read') + ->will($this->returnValue(array( + array('package' => 'pkg1', 'version' => '1.0.0-beta'), + array('package' => 'pkg2', 'version' => '0.1.10') + ))); + + $package1 = $this->createPackageMock(); + $package2 = $this->createPackageMock(); + + $repo + ->expects($this->exactly(2)) + ->method('findPackage') + ->with($this->logicalOr('pkg1', 'pkg2'), $this->logicalOr('1.0.0-beta', '0.1.10')) + ->will($this->onConsecutiveCalls($package1, $package2)); + + $this->assertEquals(array($package1, $package2), $locker->getLockedPackages()); + } + + public function testGetPackagesWithoutRepo() + { + $json = $this->createJsonFileMock(); + $repo = $this->createRepositoryManagerMock(); + + $locker = new Locker($json, $repo); + + $json + ->expects($this->once()) + ->method('exists') + ->will($this->returnValue(true)); + $json + ->expects($this->once()) + ->method('read') + ->will($this->returnValue(array( + array('package' => 'pkg1', 'version' => '1.0.0-beta'), + array('package' => 'pkg2', 'version' => '0.1.10') + ))); + + $package1 = $this->createPackageMock(); + $package2 = $this->createPackageMock(); + + $repo + ->expects($this->exactly(2)) + ->method('findPackage') + ->with($this->logicalOr('pkg1', 'pkg2'), $this->logicalOr('1.0.0-beta', '0.1.10')) + ->will($this->onConsecutiveCalls($package1, null)); + + $this->setExpectedException('LogicException'); + + $locker->getLockedPackages(); + } + + public function testLockPackages() + { + $json = $this->createJsonFileMock(); + $repo = $this->createRepositoryManagerMock(); + + $locker = new Locker($json, $repo); + + $package1 = $this->createPackageMock(); + $package2 = $this->createPackageMock(); + + $package1 + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('pkg1')); + $package1 + ->expects($this->once()) + ->method('getVersion') + ->will($this->returnValue('1.0.0-beta')); + + $package2 + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('pkg2')); + $package2 + ->expects($this->once()) + ->method('getVersion') + ->will($this->returnValue('0.1.10')); + + $json + ->expects($this->once()) + ->method('write') + ->with(array( + array('package' => 'pkg1', 'version' => '1.0.0-beta'), + array('package' => 'pkg2', 'version' => '0.1.10') + )); + + $locker->lockPackages(array($package1, $package2)); + } + + public function testLockBadPackages() + { + $json = $this->createJsonFileMock(); + $repo = $this->createRepositoryManagerMock(); + + $locker = new Locker($json, $repo); + + $package1 = $this->createPackageMock(); + $package1 + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('pkg1')); + + $this->setExpectedException('LogicException'); + + $locker->lockPackages(array($package1)); + } + + private function createJsonFileMock() + { + return $this->getMockBuilder('Composer\Json\JsonFile') + ->disableOriginalConstructor() + ->getMock(); + } + + private function createRepositoryManagerMock() + { + return $this->getMockBuilder('Composer\Repository\RepositoryManager') + ->disableOriginalConstructor() + ->getMock(); + } + + private function createPackageMock() + { + return $this->getMockBuilder('Composer\Package\PackageInterface') + ->getMock(); + } +}