├── .composer-auth.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── res ├── schema │ ├── installers-schema-1.0.json │ ├── module-schema-1.0.json │ ├── module-schema-2.0.json │ └── servers-schema-1.0.json └── template │ └── Class.tpl.php └── src ├── Api ├── AlreadyLoadedException.php ├── Asset │ ├── AssetManager.php │ ├── AssetMapping.php │ ├── DuplicateAssetMappingException.php │ └── NoSuchAssetMappingException.php ├── Config │ ├── Config.php │ ├── ConfigFile.php │ ├── ConfigFileManager.php │ ├── ConfigManager.php │ └── NoSuchConfigKeyException.php ├── Container.php ├── Context │ ├── Context.php │ └── ProjectContext.php ├── Discovery │ ├── BindingDescriptor.php │ ├── BindingState.php │ ├── BindingTypeDescriptor.php │ ├── BindingTypeState.php │ ├── DiscoveryManager.php │ ├── DiscoveryNotEmptyException.php │ ├── DuplicateBindingException.php │ ├── DuplicateTypeException.php │ ├── NoSuchBindingException.php │ ├── NoSuchTypeException.php │ └── TypeNotEnabledException.php ├── Environment.php ├── Event │ ├── AddAssetMappingEvent.php │ ├── BuildRepositoryEvent.php │ ├── GenerateFactoryEvent.php │ ├── PuliEvents.php │ └── RemoveAssetMappingEvent.php ├── Factory │ ├── FactoryManager.php │ └── Generator │ │ ├── GeneratorRegistry.php │ │ └── ServiceGenerator.php ├── FileNotFoundException.php ├── Installation │ ├── InstallationManager.php │ ├── InstallationParams.php │ └── NotInstallableException.php ├── Installer │ ├── InstallerDescriptor.php │ ├── InstallerManager.php │ ├── InstallerParameter.php │ ├── NoSuchInstallerException.php │ ├── NoSuchParameterException.php │ ├── ResourceInstaller.php │ └── Validation │ │ ├── ConstraintViolation.php │ │ └── InstallerParameterValidator.php ├── InvalidConfigException.php ├── Module │ ├── InstallInfo.php │ ├── Module.php │ ├── ModuleFile.php │ ├── ModuleList.php │ ├── ModuleManager.php │ ├── ModuleState.php │ ├── NameConflictException.php │ ├── NoSuchModuleException.php │ ├── RootModule.php │ ├── RootModuleFile.php │ ├── RootModuleFileManager.php │ └── UnsupportedVersionException.php ├── NoDirectoryException.php ├── NonRootModuleExpectedException.php ├── NotLoadedException.php ├── Php │ ├── Argument.php │ ├── Clazz.php │ ├── Import.php │ ├── Method.php │ └── ReturnValue.php ├── PuliPlugin.php ├── Repository │ ├── DuplicatePathMappingException.php │ ├── NoSuchPathMappingException.php │ ├── PathConflict.php │ ├── PathMapping.php │ ├── PathMappingState.php │ └── RepositoryManager.php ├── Server │ ├── NoSuchServerException.php │ ├── Server.php │ ├── ServerCollection.php │ └── ServerManager.php └── Storage │ ├── ReadException.php │ ├── Storage.php │ └── WriteException.php ├── Assert └── Assert.php ├── Asset ├── BindingExpressionBuilder.php └── DiscoveryAssetManager.php ├── Config ├── AbstractConfigManager.php ├── ConfigFileConverter.php ├── ConfigFileManagerImpl.php ├── DefaultConfig.php └── EnvConfig.php ├── Conflict ├── CyclicDependencyException.php ├── DependencyGraph.php ├── ModuleConflict.php ├── ModuleConflictDetector.php └── ModuleConflictException.php ├── Discovery ├── Binding │ ├── AbstractReloadBindingDescriptors.php │ ├── AddBinding.php │ ├── AddBindingDescriptorToModuleFile.php │ ├── BindingDescriptorCollection.php │ ├── DisableBindingUuid.php │ ├── EnableBindingUuid.php │ ├── LoadBindingDescriptor.php │ ├── ReloadBindingDescriptorsByTypeName.php │ ├── ReloadBindingDescriptorsByUuid.php │ ├── RemoveBindingDescriptorFromModuleFile.php │ ├── SyncBindingUuid.php │ └── UnloadBindingDescriptor.php ├── DiscoveryManagerImpl.php └── Type │ ├── AddBindingType.php │ ├── AddTypeDescriptorToModuleFile.php │ ├── BindingTypeDescriptorCollection.php │ ├── LoadTypeDescriptor.php │ ├── RemoveTypeDescriptorFromModuleFile.php │ ├── SyncTypeName.php │ ├── UnloadTypeDescriptor.php │ └── UpdateDuplicateMarksForTypeName.php ├── Factory ├── FactoryManagerImpl.php └── Generator │ ├── ChangeStream │ ├── JsonChangeStreamGenerator.php │ └── KeyValueStoreChangeStreamGenerator.php │ ├── DefaultGeneratorRegistry.php │ ├── Discovery │ ├── JsonDiscoveryGenerator.php │ └── KeyValueStoreDiscoveryGenerator.php │ ├── KeyValueStore │ ├── ArrayStoreGenerator.php │ ├── JsonFileStoreGenerator.php │ ├── NullStoreGenerator.php │ ├── PhpRedisStoreGenerator.php │ ├── PredisStoreGenerator.php │ └── RiakStoreGenerator.php │ └── Repository │ ├── FilesystemRepositoryGenerator.php │ └── JsonRepositoryGenerator.php ├── Filesystem └── FilesystemStorage.php ├── Installation └── InstallationManagerImpl.php ├── Installer ├── CopyInstaller.php ├── ModuleFileInstallerManager.php └── SymlinkInstaller.php ├── Json ├── ChainVersioner.php ├── JsonConverterProvider.php ├── JsonStorage.php └── LocalUriRetriever.php ├── Module ├── Migration │ └── ModuleFile10To20Migration.php ├── ModuleFileConverter.php ├── ModuleManagerImpl.php ├── RootModuleFileConverter.php └── RootModuleFileManagerImpl.php ├── Php └── ClassWriter.php ├── Repository ├── Mapping │ ├── AddPathMappingToModuleFile.php │ ├── ConflictCollection.php │ ├── LoadPathMapping.php │ ├── OverrideConflictingModules.php │ ├── PathMappingCollection.php │ ├── PopulateRepository.php │ ├── RemovePathMappingFromModuleFile.php │ ├── SyncRepositoryPath.php │ ├── UnloadPathMapping.php │ └── UpdateConflicts.php └── RepositoryManagerImpl.php ├── Server └── ModuleFileServerManager.php ├── Transaction ├── AtomicOperation.php ├── InterceptedOperation.php ├── OperationInterceptor.php └── Transaction.php └── Util ├── DistinguishedName.php ├── System.php └── TwoDimensionalHashMap.php /.composer-auth.json: -------------------------------------------------------------------------------- 1 | { 2 | "github-oauth": { 3 | "github.com": "PLEASE DO NOT USE THIS TOKEN IN YOUR OWN PROJECTS/FORKS", 4 | "github.com": "This token is reserved for testing the puli/* repositories", 5 | "github.com": "a9debbffdd953ee9b3b82dbc3b807cde2086bb86" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Bernhard Schussek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puli/manager", 3 | "description": "Manages the puli.json file of a Puli project.", 4 | "homepage": "http://puli.io", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Bernhard Schussek", 9 | "email": "bschussek@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^5.3.9|^7.0", 14 | "puli/repository": "^1.0-beta9", 15 | "puli/discovery": "^1.0-beta9", 16 | "puli/url-generator": "^1.0-beta4", 17 | "webmozart/json": "^1.3@dev", 18 | "webmozart/path-util": "^2.2.3", 19 | "webmozart/expression": "^1.0", 20 | "webmozart/assert": "^1.0", 21 | "webmozart/glob": "^4.0", 22 | "symfony/event-dispatcher": "^2.3|^3.0", 23 | "symfony/filesystem": "^2.3|^3.0", 24 | "ramsey/uuid": "^2.8", 25 | "psr/log": "^1.0" 26 | }, 27 | "require-dev": { 28 | "webmozart/key-value-store": "^1.0-beta7", 29 | "predis/predis": "^1.0", 30 | "basho/riak": "^1.4", 31 | "phpunit/phpunit": "^4.6", 32 | "sebastian/version": "^1.0.1", 33 | "sebastian/comparator": "^1.2" 34 | }, 35 | "suggest": { 36 | "webmozart/key-value-store": "to use key-value store change streams/discoveries", 37 | "predis/predis": "to use Predis based key-value stores", 38 | "basho/riak": "to use Riak based key-value stores" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Puli\\Manager\\": "src/" 43 | } 44 | }, 45 | "autoload-dev": { 46 | "psr-4": { 47 | "Puli\\Manager\\Tests\\": "tests/" 48 | } 49 | }, 50 | "extra": { 51 | "branch-alias": { 52 | "dev-master": "1.0-dev" 53 | } 54 | }, 55 | "support": { 56 | "issues": "https://github.com/puli/issues/issues" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /res/schema/installers-schema-1.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": { 4 | "type": "object", 5 | "properties": { 6 | "class": { 7 | "type": "string", 8 | "required": true 9 | }, 10 | "description": { 11 | "type": "string" 12 | }, 13 | "parameters": { 14 | "type": "object", 15 | "additionalProperties": { 16 | "type": "object", 17 | "properties": { 18 | "required": { 19 | "type": "boolean" 20 | }, 21 | "default": {}, 22 | "description": { 23 | "type": "string" 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /res/schema/module-schema-2.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://puli.io/schema/2.0/manager/module", 3 | "$schema": "http://json-schema.org/draft-04/schema", 4 | "type": "object", 5 | "additionalProperties": false, 6 | "properties": { 7 | "name": { 8 | "type": "string" 9 | }, 10 | "resources": { 11 | "type": "object", 12 | "additionalProperties": { 13 | "type": [ "string", "array" ] 14 | } 15 | }, 16 | "bindings": { 17 | "type": "object", 18 | "additionalProperties": { 19 | "type": "object", 20 | "properties": { 21 | "_class": { 22 | "type": "string" 23 | }, 24 | "type": { 25 | "type": "string", 26 | "required": true 27 | }, 28 | "parameters": { 29 | "type": "object", 30 | "additionalProperties": true 31 | }, 32 | "query": { 33 | "type": "string" 34 | }, 35 | "language": { 36 | "type": "string" 37 | }, 38 | "class": { 39 | "type": "string" 40 | } 41 | } 42 | } 43 | }, 44 | "binding-types": { 45 | "type": "object", 46 | "additionalProperties": { 47 | "type": "object", 48 | "properties": { 49 | "description": { 50 | "type": "string" 51 | }, 52 | "parameters": { 53 | "type": "object", 54 | "additionalProperties": { 55 | "type": "object", 56 | "properties": { 57 | "description": { 58 | "type": "string" 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | }, 66 | "depend": { 67 | "type": "array", 68 | "items": { 69 | "type": "string" 70 | } 71 | }, 72 | "order": { 73 | "type": "array", 74 | "items": { 75 | "type": "string" 76 | } 77 | }, 78 | "config": { 79 | "type": "object", 80 | "additionalProperties": true 81 | }, 82 | "plugins": { 83 | "type": "array", 84 | "items": { 85 | "type": "string" 86 | } 87 | }, 88 | "extra": { 89 | "type": "object", 90 | "additionalProperties": true 91 | }, 92 | "modules": { 93 | "type": "object", 94 | "additionalProperties": { 95 | "type": "object", 96 | "additionalProperties": false, 97 | "properties": { 98 | "install-path": { 99 | "type": "string", 100 | "required": true 101 | }, 102 | "installer": { 103 | "type": "string" 104 | }, 105 | "disabled-bindings": { 106 | "type": "array", 107 | "items": { 108 | "type": "string" 109 | } 110 | }, 111 | "env" : { 112 | "type": "string" 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /res/schema/servers-schema-1.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": { 4 | "type": "object", 5 | "properties": { 6 | "installer": { 7 | "type": "string", 8 | "required": true 9 | }, 10 | "document-root": { 11 | "type": "string", 12 | "required": true 13 | }, 14 | "url-format": { 15 | "type": "string" 16 | }, 17 | "default": { 18 | "type": "boolean" 19 | }, 20 | "parameters": { 21 | "type": "object", 22 | "additionalProperties": true 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /res/template/Class.tpl.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | getNamespaceName()): ?> 4 | namespace getNamespaceName() ?>; 5 | 6 | 7 | hasImports()): ?> 8 | getImports() as $import): ?> 9 | getNamespaceName() !== $import->getNamespaceName()): ?> 10 | use ; 11 | 12 | 13 | 14 | 15 | getDescription()): ?> 16 | /** 17 | getDescription()) as $line): ?> 18 | * 19 | 20 | */ 21 | 22 | class getShortClassName() ?>hasParentClass()): ?> extends getParentClass() ?>hasImplementedInterfaces()): ?> implements getImplementedInterfaces()) ?> 23 | { 24 | 25 | getMethods() as $method): ?> 26 | 27 | getDescription() || $method->hasArguments() || $method->hasReturnValue()): ?> 28 | /** 29 | getDescription()): ?> 30 | getDescription()) as $line): ?> 31 | * 32 | 33 | 34 | getDescription() && ($method->hasArguments() || $method->hasReturnValue())): ?> 35 | * 36 | 37 | hasArguments()): ?> 38 | getArguments() as $arg): ?> 39 | * @param getType() ?> $getName() ?>getDescription()): ?> getDescription() ?> 40 | 41 | 42 | hasArguments() && $method->hasReturnValue()): ?> 43 | * 44 | 45 | hasReturnValue()): ?> 46 | * @return getReturnValue()->getType() ?>getReturnValue()->getDescription()): ?> getReturnValue()->getDescription() ?> 47 | 48 | */ 49 | 50 | public function getName() ?>(getArguments()) ?>) 51 | { 52 | getBody()): ?> 53 | getBody()) as $line): ?> 54 | 55 | 56 | 57 | getBody() && $method->hasReturnValue()) echo "\n" ?> 58 | hasReturnValue()): ?> 59 | return getReturnValue() ?>; 60 | 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/Api/AlreadyLoadedException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when an object should be loaded that is loaded already. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class AlreadyLoadedException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/Asset/AssetMapping.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Asset; 13 | 14 | use Puli\Manager\Assert\Assert; 15 | use Rhumsaa\Uuid\Uuid; 16 | 17 | /** 18 | * Maps Puli resources to a public path on a server. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class AssetMapping 25 | { 26 | /** 27 | * @var Uuid 28 | */ 29 | private $uuid; 30 | 31 | /** 32 | * @var string 33 | */ 34 | private $glob; 35 | 36 | /** 37 | * @var string 38 | */ 39 | private $serverName; 40 | 41 | /** 42 | * @var string 43 | */ 44 | private $serverPath; 45 | 46 | /** 47 | * Creates the mapping. 48 | * 49 | * @param string $glob A glob for resources in the repository. 50 | * @param string $serverName The name of the asset server. 51 | * @param string $serverPath The path of the resource in the document root 52 | * of the server. 53 | * @param Uuid|null $uuid The UUID of the mapping. 54 | */ 55 | public function __construct($glob, $serverName, $serverPath, Uuid $uuid = null) 56 | { 57 | Assert::stringNotEmpty($glob, 'The glob must be a non-empty string. Got: %s'); 58 | Assert::stringNotEmpty($serverName, 'The server name must be a non-empty string. Got: %s'); 59 | Assert::string($serverPath, 'The public path must be a string. Got: %s'); 60 | 61 | $this->uuid = $uuid ?: Uuid::uuid4(); 62 | $this->glob = $glob; 63 | $this->serverName = $serverName; 64 | $this->serverPath = '/'.trim($serverPath, '/'); 65 | } 66 | 67 | /** 68 | * Returns the UUID of the mapping. 69 | * 70 | * @return Uuid The UUID of the mapping. 71 | */ 72 | public function getUuid() 73 | { 74 | return $this->uuid; 75 | } 76 | 77 | /** 78 | * Returns the glob for the resources in the repository. 79 | * 80 | * @return string The repository path. 81 | */ 82 | public function getGlob() 83 | { 84 | return $this->glob; 85 | } 86 | 87 | /** 88 | * Returns the name of the mapped server. 89 | * 90 | * @return string The server name. 91 | */ 92 | public function getServerName() 93 | { 94 | return $this->serverName; 95 | } 96 | 97 | /** 98 | * Returns the path of the resources relative to the server's document root. 99 | * 100 | * @return string The public resource path. 101 | */ 102 | public function getServerPath() 103 | { 104 | return $this->serverPath; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Api/Asset/DuplicateAssetMappingException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Asset; 13 | 14 | use Exception; 15 | use Rhumsaa\Uuid\Uuid; 16 | 17 | /** 18 | * Thrown when an asset mapping exists already. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class DuplicateAssetMappingException extends Exception 25 | { 26 | /** 27 | * Creates an exception for a UUID that exists already. 28 | * 29 | * @param Uuid $uuid The UUID of the mapping. 30 | * @param Exception|null $cause The exception that caused this exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forUuid(Uuid $uuid, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The asset mapping "%s" exists already.', 38 | $uuid->toString() 39 | ), 0, $cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Asset/NoSuchAssetMappingException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Asset; 13 | 14 | use Exception; 15 | use Rhumsaa\Uuid\Uuid; 16 | 17 | /** 18 | * Thrown when an asset mapping was not found. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class NoSuchAssetMappingException extends Exception 25 | { 26 | /** 27 | * Creates an exception for a UUID that was not found. 28 | * 29 | * @param Uuid $uuid The UUID of the mapping. 30 | * @param Exception|null $cause The exception that caused this exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forUuid(Uuid $uuid, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The asset mapping "%s" does not exist.', 38 | $uuid->toString() 39 | ), 0, $cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Config/ConfigFile.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Config; 13 | 14 | use InvalidArgumentException; 15 | use Puli\Manager\Assert\Assert; 16 | 17 | /** 18 | * A file storing configuration. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class ConfigFile 25 | { 26 | /** 27 | * @var string|null 28 | */ 29 | private $path; 30 | 31 | /** 32 | * @var Config 33 | */ 34 | private $config; 35 | 36 | /** 37 | * Creates a new configuration file. 38 | * 39 | * @param string|null $path The path where the configuration file is stored 40 | * or `null` if this configuration is not stored 41 | * on the file system. 42 | * @param Config|null $baseConfig The configuration that the configuration will 43 | * inherit its values from. 44 | * 45 | * @throws InvalidArgumentException If the path is not a string or empty. 46 | */ 47 | public function __construct($path = null, Config $baseConfig = null) 48 | { 49 | Assert::nullOrString($path, 'The path to the configuration file should be a string or null. Got: %s'); 50 | Assert::nullOrNotEmpty($path, 'The path to the configuration file should not be empty.'); 51 | 52 | // Inherit from default configuration 53 | $this->config = new Config($baseConfig); 54 | $this->path = $path; 55 | } 56 | 57 | /** 58 | * Returns the path to the configuration file. 59 | * 60 | * @return string|null The path or `null` if this configuration is not 61 | * stored on the file system. 62 | */ 63 | public function getPath() 64 | { 65 | return $this->path; 66 | } 67 | 68 | /** 69 | * Returns the configuration stored in the file. 70 | * 71 | * @return Config The configuration. 72 | */ 73 | public function getConfig() 74 | { 75 | return $this->config; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Api/Config/ConfigFileManager.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Config; 13 | 14 | use Puli\Manager\Api\Context\Context; 15 | 16 | /** 17 | * Manages changes to the global configuration file. 18 | * 19 | * Use this class to make persistent changes to the global config.json. 20 | * Whenever you call methods in this class, the changes will be written to disk. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | interface ConfigFileManager extends ConfigManager 27 | { 28 | /** 29 | * Returns the global context. 30 | * 31 | * @return Context The global context. 32 | */ 33 | public function getContext(); 34 | 35 | /** 36 | * Returns the managed configuration file. 37 | * 38 | * @return ConfigFile The configuration file. 39 | */ 40 | public function getConfigFile(); 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Config/NoSuchConfigKeyException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Config; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when a configuration key does not exist. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class NoSuchConfigKeyException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for a configuration key. 28 | * 29 | * @param string $key The configuration key that was not found. 30 | * @param Exception|null $cause The exception that caused this exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forKey($key, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The config key "%s" does not exist.', 38 | $key 39 | ), 0, $cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Context/Context.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Context; 13 | 14 | use Puli\Manager\Api\Config\Config; 15 | use Puli\Manager\Api\Config\ConfigFile; 16 | use Puli\Manager\Assert\Assert; 17 | use Symfony\Component\EventDispatcher\EventDispatcher; 18 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * The Puli context. 23 | * 24 | * This class contains context information for Puli. It provides access to 25 | * Puli's home directory, the global configuration and the global event 26 | * dispatcher. 27 | * 28 | * Use {@link getConfig()} to access the global configuration. 29 | * 30 | * @since 1.0 31 | * 32 | * @author Bernhard Schussek 33 | */ 34 | class Context 35 | { 36 | /** 37 | * @var string|null 38 | */ 39 | private $homeDir; 40 | 41 | /** 42 | * @var Config 43 | */ 44 | private $config; 45 | 46 | /** 47 | * @var ConfigFile|null 48 | */ 49 | private $configFile; 50 | 51 | /** 52 | * @var EventDispatcherInterface 53 | */ 54 | private $dispatcher; 55 | 56 | /** 57 | * Creates the context. 58 | * 59 | * @param string|null $homeDir The path to the home 60 | * directory or `null` if 61 | * none exists. 62 | * @param Config $config The configuration. 63 | * @param ConfigFile|null $configFile The configuration file 64 | * or `null` if none 65 | * exists. 66 | * @param EventDispatcherInterface|null $dispatcher The event dispatcher. 67 | */ 68 | public function __construct($homeDir, Config $config, ConfigFile $configFile = null, EventDispatcherInterface $dispatcher = null) 69 | { 70 | Assert::nullOrDirectory($homeDir, 'The home directory %s is not a directory.'); 71 | 72 | $this->homeDir = $homeDir ? Path::canonicalize($homeDir) : null; 73 | $this->config = $config; 74 | $this->dispatcher = $dispatcher ?: new EventDispatcher(); 75 | $this->configFile = $configFile; 76 | } 77 | 78 | /** 79 | * Returns the path to the home directory. 80 | * 81 | * This method return `null` if no home directory has been set, which 82 | * happens frequently on web servers. See 83 | * {@link System::parseHomeDirectory()} for more information. 84 | * 85 | * @return string|null The path to the home directory or `null` if none is 86 | * available. 87 | */ 88 | public function getHomeDirectory() 89 | { 90 | return $this->homeDir; 91 | } 92 | 93 | /** 94 | * Returns the configuration. 95 | * 96 | * @return Config The configuration. 97 | */ 98 | public function getConfig() 99 | { 100 | return $this->config; 101 | } 102 | 103 | /** 104 | * Returns the configuration file in the home directory. 105 | * 106 | * @return ConfigFile|null The configuration file or `null` if no home 107 | * directory was found. 108 | */ 109 | public function getConfigFile() 110 | { 111 | return $this->configFile; 112 | } 113 | 114 | /** 115 | * Returns the event dispatcher. 116 | * 117 | * @return EventDispatcherInterface The event dispatcher. 118 | */ 119 | public function getEventDispatcher() 120 | { 121 | return $this->dispatcher; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Api/Discovery/BindingState.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | /** 15 | * Contains constants representing the state of a binding. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | final class BindingState 22 | { 23 | /** 24 | * State: The binding is enabled. 25 | */ 26 | const ENABLED = 1; 27 | 28 | /** 29 | * State: The binding is disabled. 30 | */ 31 | const DISABLED = 2; 32 | 33 | /** 34 | * State: The binding's type does not exist. 35 | */ 36 | const TYPE_NOT_FOUND = 3; 37 | 38 | /** 39 | * State: The binding's type does not exist. 40 | */ 41 | const TYPE_NOT_ENABLED = 4; 42 | 43 | /** 44 | * State: The binding does not match the constraints of the binding type. 45 | */ 46 | const INVALID = 5; 47 | 48 | /** 49 | * Returns all binding states. 50 | * 51 | * @return int[] The binding states. 52 | */ 53 | public static function all() 54 | { 55 | return array( 56 | self::ENABLED, 57 | self::DISABLED, 58 | self::TYPE_NOT_FOUND, 59 | self::TYPE_NOT_ENABLED, 60 | self::INVALID, 61 | ); 62 | } 63 | 64 | /** 65 | * Must not be instantiated. 66 | */ 67 | private function __construct() 68 | { 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Api/Discovery/BindingTypeState.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | /** 15 | * Contains constants representing the state of a binding type. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | final class BindingTypeState 22 | { 23 | /** 24 | * State: The type is enabled. 25 | */ 26 | const ENABLED = 1; 27 | 28 | /** 29 | * State: The binding is disabled because it was defined twice or more. 30 | */ 31 | const DUPLICATE = 2; 32 | 33 | /** 34 | * Returns all states. 35 | * 36 | * @return int[] The states. 37 | */ 38 | public static function all() 39 | { 40 | return array( 41 | self::ENABLED, 42 | self::DUPLICATE, 43 | ); 44 | } 45 | 46 | /** 47 | * Must not be instantiated. 48 | */ 49 | private function __construct() 50 | { 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Api/Discovery/DiscoveryNotEmptyException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when the resource discovery should have been empty but was not. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class DiscoveryNotEmptyException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/Discovery/DuplicateBindingException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | use Exception; 15 | use Rhumsaa\Uuid\Uuid; 16 | use RuntimeException; 17 | 18 | /** 19 | * Thrown when a duplicate binding is detected. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class DuplicateBindingException extends RuntimeException 26 | { 27 | /** 28 | * Creates an exception for a duplicate UUID. 29 | * 30 | * @param Uuid $uuid The UUID. 31 | * @param Exception|null $cause The exception that caused this exception. 32 | * 33 | * @return static The created exception. 34 | */ 35 | public static function forUuid(Uuid $uuid, Exception $cause = null) 36 | { 37 | return new static(sprintf( 38 | 'A binding with UUID "%s" exists already.', 39 | $uuid->toString() 40 | ), 0, $cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Api/Discovery/DuplicateTypeException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when a duplicate binding type is detected. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class DuplicateTypeException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for a type name. 28 | * 29 | * @param string $typeName The name of the type. 30 | * @param Exception|null $cause The exception that caused this exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forTypeName($typeName, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The type "%s" is already defined.', 38 | $typeName 39 | ), 0, $cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Discovery/NoSuchBindingException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | use Exception; 15 | use Rhumsaa\Uuid\Uuid; 16 | use RuntimeException; 17 | 18 | /** 19 | * Thrown when a binding was not found. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class NoSuchBindingException extends RuntimeException 26 | { 27 | /** 28 | * Creates an exception for a UUID that was not found. 29 | * 30 | * @param Uuid $uuid The UUID. 31 | * @param Exception|null $cause The exception that caused this exception. 32 | * 33 | * @return static The created exception. 34 | */ 35 | public static function forUuid(Uuid $uuid, Exception $cause = null) 36 | { 37 | return new static(sprintf( 38 | 'The binding with UUID "%s" does not exist.', 39 | $uuid->toString() 40 | ), 0, $cause); 41 | } 42 | 43 | /** 44 | * Creates an exception for a UUID that was not found in a given module. 45 | * 46 | * @param Uuid $uuid The UUID. 47 | * @param string $moduleName The name of the containing module. 48 | * @param Exception|null $cause The exception that caused this 49 | * exception. 50 | * 51 | * @return static The created exception. 52 | */ 53 | public static function forUuidAndModule(Uuid $uuid, $moduleName, Exception $cause = null) 54 | { 55 | return new static(sprintf( 56 | 'The binding with UUID "%s" does not exist in module "%s".', 57 | $uuid->toString(), 58 | $moduleName 59 | ), 0, $cause); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Api/Discovery/NoSuchTypeException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when a binding type was not found. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class NoSuchTypeException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for a type name. 28 | * 29 | * @param string $typeName The name of the type. 30 | * @param Exception|null $cause The exception that caused this exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forTypeName($typeName, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The type "%s" does not exist.', 38 | $typeName 39 | ), 0, $cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Discovery/TypeNotEnabledException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Discovery; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when a type was expected to be enabled but was not. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class TypeNotEnabledException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for a type name. 28 | * 29 | * @param string $typeName The name of the type that was not 30 | * enabled. 31 | * @param Exception|null $cause The exception that caused this exception. 32 | * 33 | * @return static The created exception. 34 | */ 35 | public static function forTypeName($typeName, Exception $cause = null) 36 | { 37 | return new static(sprintf( 38 | 'The binding type "%s" is not enabled.', 39 | $typeName 40 | ), 0, $cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Api/Environment.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | /** 15 | * Contains the environment constants that Puli operates in. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | final class Environment 22 | { 23 | /** 24 | * The development environment. 25 | */ 26 | const DEV = 'dev'; 27 | 28 | /** 29 | * The production environment. 30 | */ 31 | const PROD = 'prod'; 32 | 33 | /** 34 | * Returns all environment names. 35 | * 36 | * @return string[] The environment names. 37 | */ 38 | public static function all() 39 | { 40 | return array( 41 | self::DEV, 42 | self::PROD, 43 | ); 44 | } 45 | 46 | /** 47 | * Must not be instantiated. 48 | */ 49 | private function __construct() 50 | { 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Api/Event/AddAssetMappingEvent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Event; 13 | 14 | use Puli\Manager\Api\Asset\AssetMapping; 15 | use Symfony\Component\EventDispatcher\Event; 16 | 17 | /** 18 | * Dispatched when an asset mapping is added. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class AddAssetMappingEvent extends Event 25 | { 26 | /** 27 | * @var AssetMapping 28 | */ 29 | private $mapping; 30 | 31 | /** 32 | * Creates the event. 33 | * 34 | * @param AssetMapping $mapping The asset mapping. 35 | */ 36 | public function __construct(AssetMapping $mapping) 37 | { 38 | $this->mapping = $mapping; 39 | } 40 | 41 | /** 42 | * Returns the added asset mapping. 43 | * 44 | * @return AssetMapping The asset mapping. 45 | */ 46 | public function getAssetMapping() 47 | { 48 | return $this->mapping; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Api/Event/BuildRepositoryEvent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Event; 13 | 14 | use Puli\Manager\Api\Repository\RepositoryManager; 15 | use Symfony\Component\EventDispatcher\Event; 16 | 17 | /** 18 | * Dispatched when the resource repository is built. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class BuildRepositoryEvent extends Event 25 | { 26 | /** 27 | * @var RepositoryManager 28 | */ 29 | private $repoManager; 30 | 31 | /** 32 | * @var bool 33 | */ 34 | private $buildSkipped = false; 35 | 36 | /** 37 | * Creates the event. 38 | * 39 | * @param RepositoryManager $repoManager The repository manager. 40 | */ 41 | public function __construct(RepositoryManager $repoManager) 42 | { 43 | $this->repoManager = $repoManager; 44 | } 45 | 46 | /** 47 | * Returns the repository manager. 48 | * 49 | * @return RepositoryManager The repository manager. 50 | */ 51 | public function getRepositoryManager() 52 | { 53 | return $this->repoManager; 54 | } 55 | 56 | /** 57 | * Returns whether the build of the repository should be skipped. 58 | * 59 | * @return bool Returns `true` if the build of the repository should be 60 | * skipped and `false` otherwise. 61 | */ 62 | public function isBuildSkipped() 63 | { 64 | return $this->buildSkipped; 65 | } 66 | 67 | /** 68 | * Skips build of the repository. 69 | */ 70 | public function skipBuild() 71 | { 72 | $this->buildSkipped = true; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Api/Event/GenerateFactoryEvent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Event; 13 | 14 | use Puli\Manager\Api\Php\Clazz; 15 | use Symfony\Component\EventDispatcher\Event; 16 | 17 | /** 18 | * Dispatched when the factory class is generated. 19 | * 20 | * This event is dispatched before the factory class is written to a file. 21 | * You can listen to the event to add custom code to the factory class. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class GenerateFactoryEvent extends Event 28 | { 29 | /** 30 | * @var Clazz 31 | */ 32 | private $factoryClass; 33 | 34 | /** 35 | * Creates the event. 36 | * 37 | * @param Clazz $factoryClass The factory class. 38 | */ 39 | public function __construct(Clazz $factoryClass) 40 | { 41 | $this->factoryClass = $factoryClass; 42 | } 43 | 44 | /** 45 | * Returns the factory class. 46 | * 47 | * @return Clazz The factory class. 48 | */ 49 | public function getFactoryClass() 50 | { 51 | return $this->factoryClass; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Api/Event/PuliEvents.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Event; 13 | 14 | /** 15 | * Contains the events triggered by this module. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | interface PuliEvents 22 | { 23 | /** 24 | * Dispatched when the factory class is generated. 25 | */ 26 | const GENERATE_FACTORY = 'puli.generate-factory'; 27 | 28 | /** 29 | * Dispatched before the resource repository is built. 30 | */ 31 | const PRE_BUILD_REPOSITORY = 'puli.pre-build-repository'; 32 | 33 | /** 34 | * Dispatched after the resource repository is built. 35 | */ 36 | const POST_BUILD_REPOSITORY = 'puli.post-build-repository'; 37 | 38 | /** 39 | * Dispatched after adding asset mappings. 40 | */ 41 | const POST_ADD_ASSET_MAPPING = 'puli.post-add-asset-mapping'; 42 | 43 | /** 44 | * Dispatched after removing asset mappings. 45 | */ 46 | const POST_REMOVE_ASSET_MAPPING = 'puli.post-remove-asset-mapping'; 47 | } 48 | -------------------------------------------------------------------------------- /src/Api/Event/RemoveAssetMappingEvent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Event; 13 | 14 | use Puli\Manager\Api\Asset\AssetMapping; 15 | use Symfony\Component\EventDispatcher\Event; 16 | 17 | /** 18 | * Dispatched when an asset mapping is removed. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class RemoveAssetMappingEvent extends Event 25 | { 26 | /** 27 | * @var AssetMapping 28 | */ 29 | private $mapping; 30 | 31 | /** 32 | * Creates the event. 33 | * 34 | * @param AssetMapping $mapping The asset mapping. 35 | */ 36 | public function __construct(AssetMapping $mapping) 37 | { 38 | $this->mapping = $mapping; 39 | } 40 | 41 | /** 42 | * Returns the removed asset mapping. 43 | * 44 | * @return AssetMapping The asset mapping. 45 | */ 46 | public function getAssetMapping() 47 | { 48 | return $this->mapping; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Api/Factory/Generator/GeneratorRegistry.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Factory\Generator; 13 | 14 | /** 15 | * Provides access to named service generators. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | interface GeneratorRegistry 22 | { 23 | /** 24 | * Type: A generator for implementations of {@link EditableDiscovery}. 25 | */ 26 | const DISCOVERY = 'discovery'; 27 | 28 | /** 29 | * Type: A generator for implementations of {@link EditableRepository}. 30 | */ 31 | const REPOSITORY = 'repository'; 32 | 33 | /** 34 | * Type: A generator for implementations of {@link ChangeStream}. 35 | */ 36 | const CHANGE_STREAM = 'change-stream'; 37 | 38 | /** 39 | * Type: A generator for implementations of {@link KeyValueStore}. 40 | */ 41 | const KEY_VALUE_STORE = 'key-value-store'; 42 | 43 | /** 44 | * Returns the generator for the given service name. 45 | * 46 | * @param string $type One of the type constants in this interface. 47 | * @param string $name The name of the service generator. 48 | * 49 | * @return ServiceGenerator The service generator for the given name. 50 | */ 51 | public function getServiceGenerator($type, $name); 52 | } 53 | -------------------------------------------------------------------------------- /src/Api/Factory/Generator/ServiceGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Factory\Generator; 13 | 14 | use Puli\Manager\Api\Php\Method; 15 | 16 | /** 17 | * Generates the instantiation code for a service. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | interface ServiceGenerator 24 | { 25 | /** 26 | * Generates a "new Service()" statement stored in the given variable. 27 | * 28 | * The resulting code could look something like this: 29 | * 30 | * ```php 31 | * $dependency = new Dependency(); 32 | * $varName = new Service($dependency); 33 | * ``` 34 | * 35 | * This code is added to the method passed in the second parameter. 36 | * 37 | * @param string $varName The variable name without 38 | * leading "$". 39 | * @param Method $targetMethod The method in which the code 40 | * is stored. 41 | * @param GeneratorRegistry $generatorRegistry The generator registry. 42 | * @param array $options Additional implementation 43 | * specific options. 44 | */ 45 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()); 46 | } 47 | -------------------------------------------------------------------------------- /src/Api/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when a file was not found. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class FileNotFoundException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for file path. 28 | * 29 | * @param string $path The path of the file that could not be 30 | * found. 31 | * @param Exception|null $cause The exception that caused this exception. 32 | * 33 | * @return static The created exception. 34 | */ 35 | public static function forPath($path, Exception $cause = null) 36 | { 37 | return new static(sprintf( 38 | 'The file %s does not exist.', 39 | $path 40 | ), 0, $cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Api/Installation/InstallationManager.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Installation; 13 | 14 | use Puli\Manager\Api\Asset\AssetMapping; 15 | use Puli\Repository\Api\Resource\PuliResource; 16 | 17 | /** 18 | * Manages the installation of resources. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | interface InstallationManager 25 | { 26 | /** 27 | * Prepares the installation of an asset mapping. 28 | * 29 | * If the preparation succeeds, this method returns an 30 | * {@link InstallationParams} instance which can be passed to 31 | * {@link executeInstallation()}. 32 | * 33 | * @param AssetMapping $mapping The asset mapping. 34 | * 35 | * @return InstallationParams The installation parameters. 36 | * 37 | * @throws NotInstallableException If the installation is not possible. 38 | */ 39 | public function prepareInstallation(AssetMapping $mapping); 40 | 41 | /** 42 | * Installs a resource on its server. 43 | * 44 | * @param PuliResource $resource The resource to install. 45 | * @param InstallationParams $params The installation parameters returned 46 | * by {@link prepareInstallation()}. 47 | * 48 | * @throws NotInstallableException If the installation fails. 49 | */ 50 | public function installResource(PuliResource $resource, InstallationParams $params); 51 | } 52 | -------------------------------------------------------------------------------- /src/Api/Installer/InstallerParameter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Installer; 13 | 14 | use Puli\Manager\Assert\Assert; 15 | use RuntimeException; 16 | 17 | /** 18 | * A parameter of an {@link InstallerDescriptor}. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class InstallerParameter 25 | { 26 | /** 27 | * Flag: The parameter is optional. 28 | */ 29 | const OPTIONAL = 0; 30 | 31 | /** 32 | * Flag: The parameter is required. 33 | */ 34 | const REQUIRED = 1; 35 | 36 | /** 37 | * @var string 38 | */ 39 | private $name; 40 | 41 | /** 42 | * @var int 43 | */ 44 | private $flags; 45 | 46 | /** 47 | * @var mixed 48 | */ 49 | private $defaultValue; 50 | 51 | /** 52 | * @var string|null 53 | */ 54 | private $description; 55 | 56 | /** 57 | * Creates the parameter. 58 | * 59 | * @param string $name The parameter name. 60 | * @param int $flags A bitwise combination of the flag 61 | * constants in this class. 62 | * @param null $defaultValue The default value of the parameter. Must 63 | * only be set for optional parameters. 64 | * @param string|null $description A human-readable description. 65 | */ 66 | public function __construct($name, $flags = self::OPTIONAL, $defaultValue = null, $description = null) 67 | { 68 | Assert::parameterName($name); 69 | Assert::nullOrInteger($flags, 'The parameter "$flags" must be an integer or null. Got: %s'); 70 | Assert::nullOrParameterValue($defaultValue); 71 | Assert::nullOrString($description, 'The parameter description must be a string or null. Got: %s'); 72 | Assert::nullOrNotEmpty($description, 'The parameter description must not be empty.'); 73 | 74 | if (($flags & self::REQUIRED) && null !== $defaultValue) { 75 | throw new RuntimeException('Required parameters cannot have default values.'); 76 | } 77 | 78 | $this->name = $name; 79 | $this->flags = (int) $flags; 80 | $this->defaultValue = $defaultValue; 81 | $this->description = $description; 82 | } 83 | 84 | /** 85 | * Returns the name of the parameter. 86 | * 87 | * @return string The parameter name. 88 | */ 89 | public function getName() 90 | { 91 | return $this->name; 92 | } 93 | 94 | /** 95 | * Returns the flags passed to the constructor. 96 | * 97 | * @return int A bitwise combination of the flag constants in this class. 98 | */ 99 | public function getFlags() 100 | { 101 | return $this->flags; 102 | } 103 | 104 | /** 105 | * Returns the default value of the parameter. 106 | * 107 | * @return mixed The parameter's default value. 108 | */ 109 | public function getDefaultValue() 110 | { 111 | return $this->defaultValue; 112 | } 113 | 114 | /** 115 | * Returns a human-readable description of the parameter. 116 | * 117 | * @return string|null The description or `null` if none was set. 118 | */ 119 | public function getDescription() 120 | { 121 | return $this->description; 122 | } 123 | 124 | /** 125 | * Returns whether the parameter is required. 126 | * 127 | * @return bool Returns `true` if the parameter is required and `false` 128 | * otherwise. 129 | */ 130 | public function isRequired() 131 | { 132 | return (bool) ($this->flags & self::REQUIRED); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Api/Installer/NoSuchInstallerException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Installer; 13 | 14 | use Exception; 15 | 16 | /** 17 | * Thrown when an installer was not found. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class NoSuchInstallerException extends Exception 24 | { 25 | /** 26 | * Creates an exception for an installer name that was not found. 27 | * 28 | * @param string $installerName The installer name. 29 | * @param Exception|null $cause The exception that caused this 30 | * exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forInstallerName($installerName, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The installer "%s" does not exist.', 38 | $installerName 39 | ), 0, $cause); 40 | } 41 | 42 | /** 43 | * Creates an exception for an installer name that was not found in a given 44 | * module. 45 | * 46 | * @param string $installerName The installer name. 47 | * @param string $moduleName The module name. 48 | * @param Exception|null $cause The exception that caused this 49 | * exception. 50 | * 51 | * @return static The created exception. 52 | */ 53 | public static function forInstallerNameAndModuleName($installerName, $moduleName, Exception $cause = null) 54 | { 55 | return new static(sprintf( 56 | 'The installer "%s" does not exist in module "%s".', 57 | $installerName, 58 | $moduleName 59 | ), 0, $cause); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Api/Installer/NoSuchParameterException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Installer; 13 | 14 | use Exception; 15 | 16 | /** 17 | * Thrown when an installer parameter was not found. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class NoSuchParameterException extends Exception 24 | { 25 | /** 26 | * Creates an exception for a parameter name that was not found. 27 | * 28 | * @param string $parameterName The parameter name. 29 | * @param string $installerName The installer name. 30 | * @param Exception|null $cause The exception that caused this 31 | * exception. 32 | * 33 | * @return static The created exception. 34 | */ 35 | public static function forParameterName($parameterName, $installerName, Exception $cause = null) 36 | { 37 | return new static(sprintf( 38 | 'The installer parameter "%s" does not exist for the "%s" installer.', 39 | $parameterName, 40 | $installerName 41 | ), 0, $cause); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Api/Installer/ResourceInstaller.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Installer; 13 | 14 | use Puli\Manager\Api\Installation\InstallationParams; 15 | use Puli\Manager\Api\Installation\NotInstallableException; 16 | use Puli\Repository\Api\Resource\PuliResource; 17 | 18 | /** 19 | * Installs resources on a server. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | interface ResourceInstaller 26 | { 27 | /** 28 | * Validates whether the given installation parameters can be installed. 29 | * 30 | * This method can be used to validate the parameters for an installer 31 | * independent of the actual installation process. It is guaranteed to be 32 | * called before {@link installResource()}. 33 | * 34 | * @param InstallationParams $params The installation parameters containing 35 | * all the additional information needed 36 | * to perform the installation. 37 | * 38 | * @throws NotInstallableException If the parameters are invalid. 39 | */ 40 | public function validateParams(InstallationParams $params); 41 | 42 | /** 43 | * Installs a resource on a server. 44 | * 45 | * @param PuliResource $resource The resource to install. 46 | * @param InstallationParams $params The installation parameters containing 47 | * all the additional information needed 48 | * to perform the installation. 49 | * 50 | * @throws NotInstallableException If the installation fails. 51 | */ 52 | public function installResource(PuliResource $resource, InstallationParams $params); 53 | } 54 | -------------------------------------------------------------------------------- /src/Api/Installer/Validation/ConstraintViolation.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Installer\Validation; 13 | 14 | use Webmozart\Assert\Assert; 15 | 16 | /** 17 | * A violation detected during parameter validation. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class ConstraintViolation 24 | { 25 | /** 26 | * Code: An non-existing parameter was supplied. 27 | */ 28 | const NO_SUCH_PARAMETER = 1; 29 | 30 | /** 31 | * Code: A required parameter was missing. 32 | */ 33 | const MISSING_PARAMETER = 2; 34 | 35 | /** 36 | * @var int[] 37 | */ 38 | private static $codes = array( 39 | self::NO_SUCH_PARAMETER, 40 | self::MISSING_PARAMETER, 41 | ); 42 | 43 | /** 44 | * @var int 45 | */ 46 | private $code; 47 | 48 | /** 49 | * @var mixed 50 | */ 51 | private $invalidValue; 52 | 53 | /** 54 | * @var string 55 | */ 56 | private $installerName; 57 | 58 | /** 59 | * @var string|null 60 | */ 61 | private $parameterName; 62 | 63 | /** 64 | * Creates the violation. 65 | * 66 | * @param int $code The violation code. One of the constants 67 | * defined in this class. 68 | * @param mixed $invalidValue The value that caused this violation. 69 | * @param string $installerName The name of the validated installer. 70 | * @param string|null $parameterName The name of the validated installer 71 | * parameter or `null` if this is a generic 72 | * error. 73 | */ 74 | public function __construct($code, $invalidValue, $installerName, $parameterName = null) 75 | { 76 | Assert::oneOf($code, self::$codes, 'The violation code %s is not valid.'); 77 | Assert::stringNotEmpty($installerName, 'The installer name must be a non-empty string. Got: %s'); 78 | Assert::nullOrStringNotEmpty($parameterName, 'The parameter name must be a non-empty string or null. Got: %s'); 79 | 80 | $this->code = $code; 81 | $this->installerName = $installerName; 82 | $this->parameterName = $parameterName; 83 | $this->invalidValue = $invalidValue; 84 | } 85 | 86 | /** 87 | * Returns the violation code. 88 | * 89 | * @return int One of the constants of this class. 90 | */ 91 | public function getCode() 92 | { 93 | return $this->code; 94 | } 95 | 96 | /** 97 | * Returns the value that failed validation. 98 | * 99 | * @return mixed The value that failed validation. 100 | */ 101 | public function getInvalidValue() 102 | { 103 | return $this->invalidValue; 104 | } 105 | 106 | /** 107 | * Returns the name of the validated installer. 108 | * 109 | * @return string The validated installer name. 110 | */ 111 | public function getInstallerName() 112 | { 113 | return $this->installerName; 114 | } 115 | 116 | /** 117 | * Returns the name of the validated parameter. 118 | * 119 | * @return string|null The name of the validated parameter or `null` if 120 | * this is a generic violation. 121 | */ 122 | public function getParameterName() 123 | { 124 | return $this->parameterName; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Api/Installer/Validation/InstallerParameterValidator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Installer\Validation; 13 | 14 | use Puli\Manager\Api\Installer\InstallerDescriptor; 15 | 16 | /** 17 | * Validates parameter values against the constraints defined by an installer. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class InstallerParameterValidator 24 | { 25 | /** 26 | * Returns whether the given parameter values are valid. 27 | * 28 | * @param array $parameterValues The parameter values to 29 | * validate. 30 | * @param InstallerDescriptor $descriptor The installer descriptor to 31 | * validate the values for. 32 | * 33 | * @return ConstraintViolation[] The found violations. If no violations were 34 | * found, an empty array is returned. 35 | */ 36 | public function validate(array $parameterValues, InstallerDescriptor $descriptor) 37 | { 38 | $violations = array(); 39 | 40 | foreach ($parameterValues as $name => $value) { 41 | if (!$descriptor->hasParameter($name)) { 42 | $violations[] = new ConstraintViolation( 43 | ConstraintViolation::NO_SUCH_PARAMETER, 44 | $value, 45 | $descriptor->getName(), 46 | $name 47 | ); 48 | } 49 | } 50 | 51 | foreach ($descriptor->getParameters() as $parameter) { 52 | if (!isset($parameterValues[$parameter->getName()])) { 53 | if ($parameter->isRequired()) { 54 | $violations[] = new ConstraintViolation( 55 | ConstraintViolation::MISSING_PARAMETER, 56 | $parameterValues, 57 | $descriptor->getName(), 58 | $parameter->getName() 59 | ); 60 | } 61 | } 62 | } 63 | 64 | return $violations; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Api/InvalidConfigException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when configuration is invalid. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class InvalidConfigException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/Module/ModuleState.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Module; 13 | 14 | /** 15 | * Contains constants representing the state of a module. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | final class ModuleState 22 | { 23 | /** 24 | * State: The module is enabled. 25 | */ 26 | const ENABLED = 1; 27 | 28 | /** 29 | * State: The module was not found. 30 | */ 31 | const NOT_FOUND = 2; 32 | 33 | /** 34 | * State: The module file was not loadable. 35 | */ 36 | const NOT_LOADABLE = 3; 37 | 38 | /** 39 | * Returns all states. 40 | * 41 | * @return int[] The states. 42 | */ 43 | public static function all() 44 | { 45 | return array( 46 | self::ENABLED, 47 | self::NOT_FOUND, 48 | self::NOT_LOADABLE, 49 | ); 50 | } 51 | 52 | /** 53 | * Must not be instantiated. 54 | */ 55 | private function __construct() 56 | { 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Api/Module/NameConflictException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Module; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when two modules have the same name. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class NameConflictException extends RuntimeException 25 | { 26 | /** 27 | * Creates a new exception. 28 | * 29 | * @param string $name The conflicting name. 30 | * @param Exception|null $cause The exception that caused this exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forName($name, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'A module with the name "%s" exists already.', 38 | $name 39 | ), 0, $cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Module/NoSuchModuleException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Module; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when a module was not found. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class NoSuchModuleException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/Module/RootModule.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Module; 13 | 14 | /** 15 | * The root module. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | class RootModule extends Module 22 | { 23 | /** 24 | * Creates a new root module. 25 | * 26 | * @param RootModuleFile $moduleFile The module file. 27 | * @param string $installPath The absolute install path. 28 | */ 29 | public function __construct(RootModuleFile $moduleFile, $installPath) 30 | { 31 | parent::__construct($moduleFile, $installPath); 32 | } 33 | 34 | /** 35 | * Returns the module file of the module. 36 | * 37 | * @return RootModuleFile The module file. 38 | */ 39 | public function getModuleFile() 40 | { 41 | return parent::getModuleFile(); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | protected function getDefaultName() 48 | { 49 | return '__root__'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Api/Module/UnsupportedVersionException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Module; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when the version of a module file is not supported. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class UnsupportedVersionException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for an unknown version. 28 | * 29 | * @param string $version The version that caused the 30 | * exception. 31 | * @param string[] $knownVersions The known versions. 32 | * @param string $path The path of the read module file. 33 | * @param Exception|null $cause The exception that caused this 34 | * exception. 35 | * 36 | * @return static The created exception. 37 | */ 38 | public static function forVersion($version, array $knownVersions, $path = null, Exception $cause = null) 39 | { 40 | usort($knownVersions, 'version_compare'); 41 | 42 | $isHigher = version_compare($version, end($knownVersions), '>'); 43 | 44 | return new static(sprintf( 45 | 'Cannot read module file%s at version %s. The supported versions '. 46 | 'are %s.%s', 47 | $path ? ' '.$path : '', 48 | $version, 49 | implode(', ', $knownVersions), 50 | $isHigher ? ' Please run "puli self-update".' : '' 51 | ), 0, $cause); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Api/NoDirectoryException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when a path refers to a file instead of a directory. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class NoDirectoryException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/NonRootModuleExpectedException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | use Exception; 15 | use Rhumsaa\Uuid\Uuid; 16 | use RuntimeException; 17 | 18 | /** 19 | * Thrown when an operation was performed for the root module when a non-root 20 | * module was expected. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | class NonRootModuleExpectedException extends RuntimeException 27 | { 28 | /** 29 | * Creates an exception for a binding UUID that could not be enabled in the 30 | * root module. 31 | * 32 | * @param Uuid $uuid The UUID. 33 | * @param string $moduleName The name of the module. 34 | * @param Exception|null $cause The exception that caused this 35 | * exception. 36 | * 37 | * @return static The created exception. 38 | */ 39 | public static function cannotEnableBinding(Uuid $uuid, $moduleName, Exception $cause = null) 40 | { 41 | return new static(sprintf( 42 | 'Cannot enable binding "%s" in module "%s": Can only enable '. 43 | 'bindings in non-root modules.', 44 | $uuid->toString(), 45 | $moduleName 46 | ), 0, $cause); 47 | } 48 | 49 | /** 50 | * Creates an exception for a binding UUID that could not be disabled in the 51 | * root module. 52 | * 53 | * @param Uuid $uuid The UUID. 54 | * @param string $moduleName The name of the module. 55 | * @param Exception|null $cause The exception that caused this 56 | * exception. 57 | * 58 | * @return static The created exception. 59 | */ 60 | public static function cannotDisableBinding(Uuid $uuid, $moduleName, Exception $cause = null) 61 | { 62 | return new static(sprintf( 63 | 'Cannot disable binding "%s" in module "%s": Can only disable '. 64 | 'bindings in non-root modules.', 65 | $uuid->toString(), 66 | $moduleName 67 | ), 0, $cause); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Api/NotLoadedException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when an object was expected to be loaded. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class NotLoadedException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/Php/Import.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Php; 13 | 14 | use Puli\Manager\Assert\Assert; 15 | 16 | /** 17 | * An import statement of a {@link Clazz} file. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class Import 24 | { 25 | /** 26 | * @var string 27 | */ 28 | private $namespaceName; 29 | 30 | /** 31 | * @var string 32 | */ 33 | private $shortClassName; 34 | 35 | /** 36 | * @var string|null 37 | */ 38 | private $alias; 39 | 40 | /** 41 | * Creates the import statement. 42 | * 43 | * @param string $className The fully-qualified imported class name. 44 | * @param string|null $alias If not `null`, the class will be imported 45 | * with the given alias. 46 | */ 47 | public function __construct($className, $alias = null) 48 | { 49 | Assert::stringNotEmpty($className, 'The imported class name must be a non-empty string. Got: %s'); 50 | Assert::nullOrStringNotEmpty($className, 'The import alias must be a non-empty string or null. Got: %s'); 51 | 52 | $pos = strrpos($className, '\\'); 53 | 54 | if (false === $pos) { 55 | $this->namespaceName = ''; 56 | $this->shortClassName = $className; 57 | } else { 58 | $this->namespaceName = substr($className, 0, $pos); 59 | $this->shortClassName = substr($className, $pos + 1); 60 | } 61 | 62 | $this->alias = $alias; 63 | } 64 | 65 | /** 66 | * Returns the fully-qualified class name. 67 | * 68 | * @return string The fully-qualified class name. 69 | */ 70 | public function getClassName() 71 | { 72 | return $this->namespaceName 73 | ? $this->namespaceName.'\\'.$this->shortClassName 74 | : $this->shortClassName; 75 | } 76 | 77 | /** 78 | * Returns the namespace of the imported class. 79 | * 80 | * @return string The namespace or an empty string if the class is in the 81 | * global namespace. 82 | */ 83 | public function getNamespaceName() 84 | { 85 | return $this->namespaceName; 86 | } 87 | 88 | /** 89 | * Returns the short class name of the imported class. 90 | * 91 | * @return string The short name of the imported class. 92 | */ 93 | public function getShortClassName() 94 | { 95 | return $this->shortClassName; 96 | } 97 | 98 | /** 99 | * Returns the alias under which the class is imported. 100 | * 101 | * @return string|null The alias or `null` if the class is imported under 102 | * its actual name. 103 | */ 104 | public function getAlias() 105 | { 106 | return $this->alias; 107 | } 108 | 109 | /** 110 | * Returns the source code of the import. 111 | * 112 | * @return string The source code. 113 | */ 114 | public function __toString() 115 | { 116 | return $this->alias ? $this->getClassName().' as '.$this->alias : $this->getClassName(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Api/Php/ReturnValue.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Php; 13 | 14 | use Webmozart\Assert\Assert; 15 | 16 | /** 17 | * The return value of a {@link Method}. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class ReturnValue 24 | { 25 | /** 26 | * @var string 27 | */ 28 | private $value; 29 | 30 | /** 31 | * @var string 32 | */ 33 | private $type; 34 | 35 | /** 36 | * @var string 37 | */ 38 | private $description; 39 | 40 | /** 41 | * Creates a new return value. 42 | * 43 | * @param string $value The value as source code. 44 | * @param string $type The type shown in the doc block. 45 | * @param string|null $description The doc block description. 46 | */ 47 | public function __construct($value, $type = 'mixed', $description = null) 48 | { 49 | Assert::stringNotEmpty($value, 'The return value must be a non-empty string. Got: %s'); 50 | Assert::stringNotEmpty($type, 'The return value type must be a non-empty string. Got: %s'); 51 | Assert::nullOrStringNotEmpty($description, 'The return value description must be a non-empty string or null. Got: %s'); 52 | 53 | $this->value = $value; 54 | $this->type = $type; 55 | $this->description = $description; 56 | } 57 | 58 | /** 59 | * Returns the the value. 60 | * 61 | * @return string The source code of the value. 62 | */ 63 | public function getValue() 64 | { 65 | return $this->value; 66 | } 67 | 68 | /** 69 | * Returns the type of the return value. 70 | * 71 | * @return string The return type. 72 | */ 73 | public function getType() 74 | { 75 | return $this->type; 76 | } 77 | 78 | /** 79 | * Returns the description of the return value. 80 | * 81 | * @return string The return value description. 82 | */ 83 | public function getDescription() 84 | { 85 | return $this->description; 86 | } 87 | 88 | /** 89 | * Returns the source code of the return value. 90 | * 91 | * @return string The source code. 92 | */ 93 | public function __toString() 94 | { 95 | return $this->value; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Api/PuliPlugin.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api; 13 | 14 | /** 15 | * A plugin for the repository manager. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | interface PuliPlugin 22 | { 23 | /** 24 | * Activates the plugin. 25 | * 26 | * @param Container $container The Puli service container. 27 | */ 28 | public function activate(Container $container); 29 | } 30 | -------------------------------------------------------------------------------- /src/Api/Repository/DuplicatePathMappingException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Repository; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when a duplicate path mapping is detected. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class DuplicatePathMappingException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for a duplicate repository path. 28 | * 29 | * @param string $repositoryPath The mapped repository path. 30 | * @param string $moduleName The name of the module containing 31 | * the mapping. 32 | * @param Exception|null $cause The exception that caused this 33 | * exception. 34 | * 35 | * @return static The created exception. 36 | */ 37 | public static function forRepositoryPath($repositoryPath, $moduleName, Exception $cause = null) 38 | { 39 | return new static(sprintf( 40 | 'The path "%s" is already mapped in module "%s".', 41 | $repositoryPath, 42 | $moduleName 43 | ), 0, $cause); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Api/Repository/NoSuchPathMappingException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Repository; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when a path mapping was not found. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class NoSuchPathMappingException extends RuntimeException 25 | { 26 | /** 27 | * Creates an exception for a repository path. 28 | * 29 | * @param string $path The repository path. 30 | * @param Exception|null $cause The exception that caused this exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forRepositoryPath($path, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The repository path "%s" is not mapped.', 38 | $path 39 | ), 0, $cause); 40 | } 41 | 42 | /** 43 | * Creates an exception for a repository path and a module name. 44 | * 45 | * @param string $path The repository path. 46 | * @param string $moduleName The name of the containing module. 47 | * @param Exception|null $cause The exception that caused this 48 | * exception. 49 | * 50 | * @return static The created exception. 51 | */ 52 | public static function forRepositoryPathAndModule($path, $moduleName, Exception $cause = null) 53 | { 54 | return new static(sprintf( 55 | 'The repository path "%s" is not mapped in module "%s".', 56 | $path, 57 | $moduleName 58 | ), 0, $cause); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Api/Repository/PathMappingState.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Repository; 13 | 14 | /** 15 | * Contains constants representing the state of a path mapping. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | final class PathMappingState 22 | { 23 | /** 24 | * State: The mapping is enabled. 25 | */ 26 | const ENABLED = 1; 27 | 28 | /** 29 | * State: The path referenced by the mapping was not found. 30 | */ 31 | const NOT_FOUND = 2; 32 | 33 | /** 34 | * State: The mapping conflicts with a mapping in another module. 35 | */ 36 | const CONFLICT = 3; 37 | 38 | /** 39 | * Returns all mapping states. 40 | * 41 | * @return int[] The mapping states. 42 | */ 43 | public static function all() 44 | { 45 | return array( 46 | self::ENABLED, 47 | self::NOT_FOUND, 48 | self::CONFLICT, 49 | ); 50 | } 51 | 52 | /** 53 | * Must not be instantiated. 54 | */ 55 | private function __construct() 56 | { 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Api/Server/NoSuchServerException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Server; 13 | 14 | use Exception; 15 | 16 | /** 17 | * Thrown when a server was not found. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class NoSuchServerException extends Exception 24 | { 25 | /** 26 | * Creates an exception for a server name that was not found. 27 | * 28 | * @param string $serverName The server name. 29 | * @param Exception|null $cause The exception that caused this 30 | * exception. 31 | * 32 | * @return static The created exception. 33 | */ 34 | public static function forServerName($serverName, Exception $cause = null) 35 | { 36 | return new static(sprintf( 37 | 'The asset server "%s" does not exist.', 38 | $serverName 39 | ), 0, $cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Api/Server/ServerManager.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Server; 13 | 14 | use Puli\Manager\Api\Installer\NoSuchInstallerException; 15 | use Webmozart\Expression\Expression; 16 | 17 | /** 18 | * Manages the asset servers of the application. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | interface ServerManager 25 | { 26 | /** 27 | * Adds a server. 28 | * 29 | * If a server with the same name exists, the existing server is 30 | * overwritten. 31 | * 32 | * @param Server $server The server to add. 33 | * 34 | * @throws NoSuchInstallerException If the installer referred to by the 35 | * server does not exist. 36 | */ 37 | public function addServer(Server $server); 38 | 39 | /** 40 | * Removes a server. 41 | * 42 | * If the server does not exist, this method does nothing. 43 | * 44 | * @param string $serverName The name of the server. 45 | */ 46 | public function removeServer($serverName); 47 | 48 | /** 49 | * Removes all servers matching the given expression. 50 | * 51 | * If no matching servers are found, this method does nothing. 52 | * 53 | * @param Expression $expr The search criteria. 54 | */ 55 | public function removeServers(Expression $expr); 56 | 57 | /** 58 | * Removes all servers. 59 | * 60 | * If no servers are found, this method does nothing. 61 | */ 62 | public function clearServers(); 63 | 64 | /** 65 | * Returns the server with the given name. 66 | * 67 | * @param string $serverName The name of the server. 68 | * 69 | * @return Server The server. 70 | * 71 | * @throws NoSuchServerException If the server does not exist. 72 | */ 73 | public function getServer($serverName); 74 | 75 | /** 76 | * Returns all servers. 77 | * 78 | * @return ServerCollection The servers. 79 | */ 80 | public function getServers(); 81 | 82 | /** 83 | * Returns all servers matching the given expression. 84 | * 85 | * @param Expression $expr The search criteria. 86 | * 87 | * @return ServerCollection The servers. 88 | */ 89 | public function findServers(Expression $expr); 90 | 91 | /** 92 | * Returns whether a server exists. 93 | * 94 | * @param string $serverName The name of the server. 95 | * 96 | * @return bool Returns `true` if the server exists and `false` otherwise. 97 | */ 98 | public function hasServer($serverName); 99 | 100 | /** 101 | * Returns whether the manager has any servers. 102 | * 103 | * You can optionally pass an expression to check whether the manager has 104 | * servers matching that expression. 105 | * 106 | * @param Expression|null $expr The search criteria. 107 | * 108 | * @return bool Returns `true` if the manager has servers and `false` 109 | * otherwise. 110 | */ 111 | public function hasServers(Expression $expr = null); 112 | } 113 | -------------------------------------------------------------------------------- /src/Api/Storage/ReadException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Storage; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when files could not be read. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class ReadException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/Storage/Storage.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Storage; 13 | 14 | use Puli\Manager\Api\FileNotFoundException; 15 | 16 | /** 17 | * Stores files. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | interface Storage 24 | { 25 | /** 26 | * Reads a file. 27 | * 28 | * @param string $path The file path. 29 | * 30 | * @return string The file contents. 31 | * 32 | * @throws FileNotFoundException If the file cannot be found. 33 | * @throws ReadException If the file cannot be read. 34 | */ 35 | public function read($path); 36 | 37 | /** 38 | * Writes a file. 39 | * 40 | * @param string $path The file path. 41 | * @param string $contents The contents to write to the file. 42 | * 43 | * @return int The number of bytes written to the file. 44 | * 45 | * @throws WriteException If the file cannot be written. 46 | */ 47 | public function write($path, $contents); 48 | 49 | /** 50 | * Returns whether a path exists. 51 | * 52 | * @param string $path The file path. 53 | * 54 | * @return bool Returns `true` if the path exists and `false` otherwise. 55 | */ 56 | public function exists($path); 57 | } 58 | -------------------------------------------------------------------------------- /src/Api/Storage/WriteException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Api\Storage; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when files could not be written. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class WriteException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Config/ConfigFileConverter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Config; 13 | 14 | use Puli\Manager\Api\Config\ConfigFile; 15 | use Puli\Manager\Assert\Assert; 16 | use Webmozart\Json\Conversion\JsonConverter; 17 | 18 | /** 19 | * Converts config files to JSON and back. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class ConfigFileConverter implements JsonConverter 26 | { 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function toJson($configFile, array $options = array()) 31 | { 32 | Assert::isInstanceOf($configFile, 'Puli\Manager\Api\Config\ConfigFile'); 33 | 34 | $jsonData = array(); 35 | 36 | foreach ($configFile->getConfig()->toRawArray(false) as $key => $value) { 37 | $jsonData[$key] = $value; 38 | } 39 | 40 | // The topmost array is always an object, even if empty 41 | return (object) $this->arraysToObjects($jsonData); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function fromJson($jsonData, array $options = array()) 48 | { 49 | $path = isset($options['path']) ? $options['path'] : null; 50 | $baseConfig = isset($options['baseConfig']) ? $options['baseConfig'] : null; 51 | 52 | Assert::isInstanceOf($jsonData, 'stdClass'); 53 | Assert::nullOrString($path, 'The "path" option should be null or a string. Got: %s'); 54 | Assert::nullOrIsInstanceOf($baseConfig, 'Puli\Manager\Api\Config\Config', 'The "baseConfig" option should be null or an instance of %2$s. Got: %s'); 55 | 56 | $configFile = new ConfigFile($path, $baseConfig); 57 | $config = $configFile->getConfig(); 58 | 59 | $jsonData = $this->objectsToArrays($jsonData); 60 | 61 | foreach ($jsonData as $key => $value) { 62 | $config->set($key, $value); 63 | } 64 | 65 | return $configFile; 66 | } 67 | 68 | private function objectsToArrays($data) 69 | { 70 | $data = (array) $data; 71 | 72 | foreach ($data as $key => $value) { 73 | $data[$key] = is_object($value) ? $this->objectsToArrays($value) : $value; 74 | } 75 | 76 | return $data; 77 | } 78 | 79 | private function arraysToObjects(array $data) 80 | { 81 | $intKeys = true; 82 | 83 | foreach ($data as $key => $value) { 84 | $intKeys = $intKeys && is_int($key); 85 | $data[$key] = is_array($value) ? $this->arraysToObjects($value) : $value; 86 | } 87 | 88 | return $intKeys ? $data : (object) $data; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Config/ConfigFileManagerImpl.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Config; 13 | 14 | use Puli\Manager\Api\Config\ConfigFile; 15 | use Puli\Manager\Api\Config\ConfigFileManager; 16 | use Puli\Manager\Api\Context\Context; 17 | use Puli\Manager\Json\JsonStorage; 18 | 19 | /** 20 | * Manages changes to the global configuration file. 21 | * 22 | * Use this class to make persistent changes to the global config.json. 23 | * Whenever you call methods in this class, the changes will be written to disk. 24 | * 25 | * @since 1.0 26 | * 27 | * @author Bernhard Schussek 28 | */ 29 | class ConfigFileManagerImpl extends AbstractConfigManager implements ConfigFileManager 30 | { 31 | /** 32 | * @var Context 33 | */ 34 | private $context; 35 | 36 | /** 37 | * @var ConfigFile 38 | */ 39 | private $configFile; 40 | 41 | /** 42 | * @var JsonStorage 43 | */ 44 | private $jsonStorage; 45 | 46 | /** 47 | * Creates the configuration manager. 48 | * 49 | * @param Context $context The global context. 50 | * @param JsonStorage $jsonStorage The configuration file storage. 51 | */ 52 | public function __construct(Context $context, JsonStorage $jsonStorage) 53 | { 54 | $this->context = $context; 55 | $this->jsonStorage = $jsonStorage; 56 | $this->configFile = $context->getConfigFile(); 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function getConfig() 63 | { 64 | return $this->configFile->getConfig(); 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function getContext() 71 | { 72 | return $this->context; 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function getConfigFile() 79 | { 80 | return $this->configFile; 81 | } 82 | 83 | /** 84 | * {@inheritdoc} 85 | */ 86 | protected function saveConfigFile() 87 | { 88 | $this->jsonStorage->saveConfigFile($this->configFile); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Config/DefaultConfig.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Config; 13 | 14 | use Puli\Manager\Api\Config\Config; 15 | 16 | /** 17 | * Stores default configuration values. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class DefaultConfig extends Config 24 | { 25 | /** 26 | * Creates the default configuration. 27 | */ 28 | public function __construct() 29 | { 30 | parent::__construct(null, array( 31 | self::PULI_DIR => '.puli', 32 | self::FACTORY_AUTO_GENERATE => true, 33 | self::FACTORY_OUT_CLASS => 'Puli\GeneratedPuliFactory', 34 | self::FACTORY_OUT_FILE => '{$puli-dir}/GeneratedPuliFactory.php', 35 | self::FACTORY_IN_CLASS => '{$factory.out.class}', 36 | self::FACTORY_IN_FILE => '{$factory.out.file}', 37 | self::REPOSITORY_TYPE => 'json', 38 | self::REPOSITORY_PATH => '{$puli-dir}/path-mappings.json', 39 | self::REPOSITORY_SYMLINK => true, 40 | self::REPOSITORY_OPTIMIZE => false, 41 | self::REPOSITORY_STORE_CACHE => true, 42 | self::CHANGE_STREAM_TYPE => 'json', 43 | self::CHANGE_STREAM_PATH => '{$puli-dir}/change-stream.json', 44 | self::CHANGE_STREAM_STORE_CACHE => true, 45 | self::DISCOVERY_TYPE => 'json', 46 | self::DISCOVERY_PATH => '{$puli-dir}/bindings.json', 47 | self::DISCOVERY_STORE_CACHE => true, 48 | )); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Config/EnvConfig.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Config; 13 | 14 | use Puli\Manager\Api\Config\Config; 15 | 16 | /** 17 | * Loads configuration values from context variables. 18 | * 19 | * Configuration keys that are not set as context variables are loaded from 20 | * the base configuration passed to the constructor. 21 | * 22 | * Currently, only the context variable "PULI_DIR" is supported by this 23 | * class. 24 | * 25 | * @since 1.0 26 | * 27 | * @author Bernhard Schussek 28 | */ 29 | class EnvConfig extends Config 30 | { 31 | /** 32 | * Creates the configuration. 33 | * 34 | * @param Config|null $baseConfig The base configuration to use for unset 35 | * values. 36 | */ 37 | public function __construct(Config $baseConfig = null) 38 | { 39 | parent::__construct($baseConfig); 40 | 41 | if (false !== ($puliDir = getenv('PULI_DIR'))) { 42 | $this->set(Config::PULI_DIR, $puliDir); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Conflict/CyclicDependencyException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Conflict; 13 | 14 | use RuntimeException; 15 | 16 | /** 17 | * Thrown when a cyclic dependency is detected in the dependency graph. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class CyclicDependencyException extends RuntimeException 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Conflict/ModuleConflict.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Conflict; 13 | 14 | /** 15 | * A conflict between two modules claiming the same token. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | * 21 | * @see ModuleConflictDetector 22 | */ 23 | class ModuleConflict 24 | { 25 | /** 26 | * @var string 27 | */ 28 | private $conflictingToken; 29 | 30 | /** 31 | * @var string[] 32 | */ 33 | private $moduleNames; 34 | 35 | /** 36 | * Creates the conflict. 37 | * 38 | * @param string $conflictingToken The token that caused the conflict. 39 | * @param string[] $moduleNames The names of the modules claiming the 40 | * token. 41 | */ 42 | public function __construct($conflictingToken, array $moduleNames) 43 | { 44 | sort($moduleNames); 45 | 46 | $this->conflictingToken = $conflictingToken; 47 | $this->moduleNames = array(); 48 | 49 | foreach ($moduleNames as $moduleName) { 50 | $this->moduleNames[$moduleName] = true; 51 | } 52 | } 53 | 54 | /** 55 | * Returns the conflicting repository path. 56 | * 57 | * @return string The conflicting repository path. 58 | */ 59 | public function getConflictingToken() 60 | { 61 | return $this->conflictingToken; 62 | } 63 | 64 | /** 65 | * Returns the names of the modules causing the conflict. 66 | * 67 | * @return string[] The name of the first conflicting module. 68 | */ 69 | public function getModuleNames() 70 | { 71 | return array_keys($this->moduleNames); 72 | } 73 | 74 | /** 75 | * Returns whether the conflict involves a given module name. 76 | * 77 | * @param string $moduleName A module name. 78 | * 79 | * @return bool Returns `true` if the module caused the conflict. 80 | */ 81 | public function involvesModule($moduleName) 82 | { 83 | return isset($this->moduleNames[$moduleName]); 84 | } 85 | 86 | /** 87 | * Returns the opposing module names in the conflict. 88 | * 89 | * @param string $moduleName The name of a module. 90 | * 91 | * @return string[] Returns the names of the opposing modules or an empty 92 | * array if the module is not involved in the conflict. 93 | */ 94 | public function getOpponents($moduleName) 95 | { 96 | if (!isset($this->moduleNames[$moduleName])) { 97 | return array(); 98 | } 99 | 100 | $opponents = $this->moduleNames; 101 | 102 | unset($opponents[$moduleName]); 103 | 104 | return array_keys($opponents); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Conflict/ModuleConflictException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Conflict; 13 | 14 | use Exception; 15 | use RuntimeException; 16 | 17 | /** 18 | * Thrown when two modules have conflicting path mappings. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class ModuleConflictException extends RuntimeException 25 | { 26 | public static function forPathConflict(ModuleConflict $conflict, Exception $cause = null) 27 | { 28 | $moduleNames = $conflict->getModuleNames(); 29 | $lastModuleName = array_pop($moduleNames); 30 | 31 | return new static(sprintf( 32 | 'The modules "%s" and "%s" add resources for the same path '. 33 | "\"%s\", but have no override order defined between them.\n\n". 34 | "Resolutions:\n\n(1) Add the key \"override\" to the puli.json ". 35 | "of one module and set its value to the other module name.\n(2) ". 36 | 'Add the key "override-order" to the puli.json of the root '. 37 | 'module and define the order of the modules there.', 38 | implode('", "', $moduleNames), 39 | $lastModuleName, 40 | $conflict->getConflictingToken() 41 | ), 0, $cause); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Discovery/Binding/AbstractReloadBindingDescriptors.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Api\Discovery\BindingDescriptor; 15 | use Puli\Manager\Discovery\Type\BindingTypeDescriptorCollection; 16 | use Puli\Manager\Transaction\OperationInterceptor; 17 | 18 | /** 19 | * Base class for reload operations. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | abstract class AbstractReloadBindingDescriptors implements OperationInterceptor 26 | { 27 | /** 28 | * @var BindingTypeDescriptorCollection 29 | */ 30 | private $typeDescriptors; 31 | 32 | /** 33 | * @var BindingDescriptor[] 34 | */ 35 | private $reloadedDescriptors = array(); 36 | 37 | /** 38 | * @param BindingTypeDescriptorCollection $typeDescriptors 39 | */ 40 | public function __construct(BindingTypeDescriptorCollection $typeDescriptors) 41 | { 42 | $this->typeDescriptors = $typeDescriptors; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function postExecute() 49 | { 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function postRollback() 56 | { 57 | foreach ($this->reloadedDescriptors as $bindingDescriptor) { 58 | $this->reloadBindingDescriptor($bindingDescriptor); 59 | } 60 | } 61 | 62 | /** 63 | * Unloads and loads a binding descriptor. 64 | * 65 | * The descriptor is remembered and reloaded again in {@link postRollback()} 66 | * if the intercepted operation needs to be rolled back. 67 | * 68 | * @param BindingDescriptor $bindingDescriptor The descriptor to reload. 69 | */ 70 | protected function reloadBindingDescriptor(BindingDescriptor $bindingDescriptor) 71 | { 72 | if (!$bindingDescriptor->isLoaded()) { 73 | return; 74 | } 75 | 76 | // Keep backup of containing module before calling unload() 77 | $containingModule = $bindingDescriptor->getContainingModule(); 78 | $typeName = $bindingDescriptor->getTypeName(); 79 | $typeDescriptor = $this->typeDescriptors->getEnabled($typeName); 80 | 81 | // never fails with the check in the beginning 82 | $bindingDescriptor->unload(); 83 | 84 | // never fails after unloading, given that the type name matches 85 | // (which we can guarantee here) 86 | $bindingDescriptor->load($containingModule, $typeDescriptor); 87 | 88 | $this->reloadedDescriptors[] = $bindingDescriptor; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Discovery/Binding/AddBinding.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Discovery\Api\EditableDiscovery; 15 | use Puli\Manager\Api\Discovery\BindingDescriptor; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Binds a binding descriptor to the resource discovery. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class AddBinding implements AtomicOperation 26 | { 27 | /** 28 | * @var BindingDescriptor 29 | */ 30 | private $bindingDescriptor; 31 | 32 | /** 33 | * @var EditableDiscovery 34 | */ 35 | private $discovery; 36 | 37 | public function __construct(BindingDescriptor $bindingDescriptor, EditableDiscovery $discovery) 38 | { 39 | // Clone so that rollback() works if the binding is unloaded 40 | $this->bindingDescriptor = clone $bindingDescriptor; 41 | $this->discovery = $discovery; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function execute() 48 | { 49 | $this->discovery->addBinding($this->bindingDescriptor->getBinding()); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function rollback() 56 | { 57 | $this->discovery->removeBinding($this->bindingDescriptor->getUuid()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Discovery/Binding/AddBindingDescriptorToModuleFile.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Api\Discovery\BindingDescriptor; 15 | use Puli\Manager\Api\Module\RootModuleFile; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Adds a binding descriptor to the root module file. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class AddBindingDescriptorToModuleFile implements AtomicOperation 26 | { 27 | /** 28 | * @var BindingDescriptor 29 | */ 30 | private $bindingDescriptor; 31 | 32 | /** 33 | * @var RootModuleFile 34 | */ 35 | private $rootModuleFile; 36 | 37 | /** 38 | * @var BindingDescriptor 39 | */ 40 | private $previousDescriptor; 41 | 42 | public function __construct(BindingDescriptor $bindingDescriptor, RootModuleFile $rootModuleFile) 43 | { 44 | $this->bindingDescriptor = $bindingDescriptor; 45 | $this->rootModuleFile = $rootModuleFile; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function execute() 52 | { 53 | $uuid = $this->bindingDescriptor->getUuid(); 54 | 55 | if ($this->rootModuleFile->hasBindingDescriptor($uuid)) { 56 | $this->previousDescriptor = $this->rootModuleFile->getBindingDescriptor($uuid); 57 | } 58 | 59 | $this->rootModuleFile->addBindingDescriptor($this->bindingDescriptor); 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function rollback() 66 | { 67 | if ($this->previousDescriptor) { 68 | $this->rootModuleFile->addBindingDescriptor($this->previousDescriptor); 69 | } else { 70 | $this->rootModuleFile->removeBindingDescriptor($this->bindingDescriptor->getUuid()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Discovery/Binding/BindingDescriptorCollection.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use OutOfBoundsException; 15 | use Puli\Manager\Api\Discovery\BindingDescriptor; 16 | use Rhumsaa\Uuid\Uuid; 17 | 18 | /** 19 | * A collection of binding descriptors. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class BindingDescriptorCollection 26 | { 27 | /** 28 | * @var BindingDescriptor[] 29 | */ 30 | private $map; 31 | 32 | /** 33 | * Creates the store. 34 | */ 35 | public function __construct() 36 | { 37 | $this->map = array(); 38 | } 39 | 40 | /** 41 | * Adds a binding descriptor. 42 | * 43 | * @param BindingDescriptor $bindingDescriptor The binding descriptor. 44 | */ 45 | public function add(BindingDescriptor $bindingDescriptor) 46 | { 47 | $this->map[$bindingDescriptor->getUuid()->toString()] = $bindingDescriptor; 48 | } 49 | 50 | /** 51 | * Removes a binding descriptor. 52 | * 53 | * This method ignores non-existing binding descriptors. 54 | * 55 | * @param Uuid $uuid The UUID of the binding descriptor. 56 | */ 57 | public function remove(Uuid $uuid) 58 | { 59 | unset($this->map[$uuid->toString()]); 60 | } 61 | 62 | /** 63 | * Returns a binding descriptor. 64 | * 65 | * @param Uuid $uuid The UUID of the binding descriptor. 66 | * 67 | * @return BindingDescriptor The binding descriptor. 68 | * 69 | * @throws OutOfBoundsException If no binding descriptor was set for the 70 | * given UUID. 71 | */ 72 | public function get(Uuid $uuid) 73 | { 74 | if (!isset($this->map[$uuid->toString()])) { 75 | throw new OutOfBoundsException(sprintf( 76 | 'The binding with UUID "%s" does not exist.', 77 | $uuid->toString() 78 | )); 79 | } 80 | 81 | return $this->map[$uuid->toString()]; 82 | } 83 | 84 | /** 85 | * Returns whether a binding descriptor exists. 86 | * 87 | * @param Uuid $uuid The UUID of the binding descriptor. 88 | * 89 | * @return bool Returns `true` if a binding descriptor was set for the given 90 | * UUID. 91 | */ 92 | public function contains(Uuid $uuid) 93 | { 94 | return isset($this->map[$uuid->toString()]); 95 | } 96 | 97 | /** 98 | * Returns the UUIDs of all binding descriptors. 99 | * 100 | * @return Uuid[] The UUIDs of the stored bindings. 101 | */ 102 | public function getUuids() 103 | { 104 | $uuids = array(); 105 | 106 | foreach ($this->map as $bindingDescriptor) { 107 | $uuids[] = $bindingDescriptor->getUuid(); 108 | } 109 | 110 | return $uuids; 111 | } 112 | 113 | /** 114 | * Returns the contents of the collection as array. 115 | * 116 | * @return BindingDescriptor[] An array containing all bindings indexed by UUID. 117 | */ 118 | public function toArray() 119 | { 120 | return $this->map; 121 | } 122 | 123 | /** 124 | * Returns whether the collection is empty. 125 | * 126 | * @return bool Returns `true` if the collection is empty and `false` 127 | * otherwise. 128 | */ 129 | public function isEmpty() 130 | { 131 | return 0 === count($this->map); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Discovery/Binding/DisableBindingUuid.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Api\Module\InstallInfo; 15 | use Puli\Manager\Transaction\AtomicOperation; 16 | use Rhumsaa\Uuid\Uuid; 17 | 18 | /** 19 | * Disables a binding descriptor for a given install info. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class DisableBindingUuid implements AtomicOperation 26 | { 27 | /** 28 | * @var Uuid 29 | */ 30 | private $uuid; 31 | 32 | /** 33 | * @var InstallInfo 34 | */ 35 | private $installInfo; 36 | 37 | /** 38 | * @var bool 39 | */ 40 | private $wasDisabled = true; 41 | 42 | public function __construct(Uuid $uuid, InstallInfo $installInfo) 43 | { 44 | $this->uuid = $uuid; 45 | $this->installInfo = $installInfo; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function execute() 52 | { 53 | if (!$this->installInfo->hasDisabledBindingUuid($this->uuid)) { 54 | $this->wasDisabled = false; 55 | $this->installInfo->addDisabledBindingUuid($this->uuid); 56 | } 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function rollback() 63 | { 64 | if (!$this->wasDisabled) { 65 | $this->installInfo->removeDisabledBindingUuid($this->uuid); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Discovery/Binding/EnableBindingUuid.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Api\Module\InstallInfo; 15 | use Puli\Manager\Transaction\AtomicOperation; 16 | use Rhumsaa\Uuid\Uuid; 17 | 18 | /** 19 | * Enables a binding descriptor for a given install info. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class EnableBindingUuid implements AtomicOperation 26 | { 27 | /** 28 | * @var Uuid 29 | */ 30 | private $uuid; 31 | 32 | /** 33 | * @var InstallInfo 34 | */ 35 | private $installInfo; 36 | 37 | /** 38 | * @var bool 39 | */ 40 | private $wasDisabled = false; 41 | 42 | public function __construct(Uuid $uuid, InstallInfo $installInfo) 43 | { 44 | $this->uuid = $uuid; 45 | $this->installInfo = $installInfo; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function execute() 52 | { 53 | if ($this->installInfo->hasDisabledBindingUuid($this->uuid)) { 54 | $this->wasDisabled = true; 55 | $this->installInfo->removeDisabledBindingUuid($this->uuid); 56 | } 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function rollback() 63 | { 64 | if ($this->wasDisabled) { 65 | $this->installInfo->addDisabledBindingUuid($this->uuid); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Discovery/Binding/LoadBindingDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Api\Discovery\BindingDescriptor; 15 | use Puli\Manager\Api\Module\Module; 16 | use Puli\Manager\Discovery\Type\BindingTypeDescriptorCollection; 17 | use Puli\Manager\Transaction\AtomicOperation; 18 | 19 | /** 20 | * Loads a binding descriptor. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | class LoadBindingDescriptor implements AtomicOperation 27 | { 28 | /** 29 | * @var BindingDescriptor 30 | */ 31 | private $bindingDescriptor; 32 | 33 | /** 34 | * @var Module 35 | */ 36 | private $containingModule; 37 | 38 | /** 39 | * @var BindingDescriptorCollection 40 | */ 41 | private $bindingDescriptors; 42 | 43 | /** 44 | * @var BindingTypeDescriptorCollection 45 | */ 46 | private $typeDescriptors; 47 | 48 | /** 49 | * @var BindingDescriptor 50 | */ 51 | private $previousDescriptor; 52 | 53 | public function __construct(BindingDescriptor $bindingDescriptor, Module $containingModule, BindingDescriptorCollection $bindingDescriptors, BindingTypeDescriptorCollection $typeDescriptors) 54 | { 55 | $this->bindingDescriptor = $bindingDescriptor; 56 | $this->containingModule = $containingModule; 57 | $this->bindingDescriptors = $bindingDescriptors; 58 | $this->typeDescriptors = $typeDescriptors; 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function execute() 65 | { 66 | // sanity check 67 | if ($this->bindingDescriptor->isLoaded()) { 68 | return; 69 | } 70 | 71 | $typeName = $this->bindingDescriptor->getTypeName(); 72 | $typeDescriptor = $this->typeDescriptors->contains($typeName) 73 | ? $this->typeDescriptors->getFirst($typeName) 74 | : null; 75 | 76 | $this->bindingDescriptor->load($this->containingModule, $typeDescriptor); 77 | 78 | $uuid = $this->bindingDescriptor->getUuid(); 79 | 80 | if ($this->bindingDescriptors->contains($uuid)) { 81 | $this->previousDescriptor = $this->bindingDescriptors->get($uuid); 82 | } 83 | 84 | $this->bindingDescriptors->add($this->bindingDescriptor); 85 | } 86 | 87 | /** 88 | * {@inheritdoc} 89 | */ 90 | public function rollback() 91 | { 92 | // sanity check 93 | if (!$this->bindingDescriptor->isLoaded()) { 94 | return; 95 | } 96 | 97 | // never fails with the check before 98 | $this->bindingDescriptor->unload(); 99 | 100 | if ($this->previousDescriptor) { 101 | // never fails 102 | $this->bindingDescriptors->add($this->previousDescriptor); 103 | } else { 104 | // never fails 105 | $this->bindingDescriptors->remove($this->bindingDescriptor->getUuid()); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Discovery/Binding/ReloadBindingDescriptorsByTypeName.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Discovery\Type\BindingTypeDescriptorCollection; 15 | 16 | /** 17 | * Reloads all binding descriptors with a given type name. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class ReloadBindingDescriptorsByTypeName extends AbstractReloadBindingDescriptors 24 | { 25 | /** 26 | * @var string 27 | */ 28 | private $typeName; 29 | 30 | /** 31 | * @var BindingDescriptorCollection 32 | */ 33 | private $bindingDescriptors; 34 | 35 | /** 36 | * @param string $typeName 37 | * @param BindingDescriptorCollection $bindingDescriptors 38 | * @param BindingTypeDescriptorCollection $typeDescriptors 39 | */ 40 | public function __construct($typeName, BindingDescriptorCollection $bindingDescriptors, BindingTypeDescriptorCollection $typeDescriptors) 41 | { 42 | parent::__construct($typeDescriptors); 43 | 44 | $this->typeName = $typeName; 45 | $this->bindingDescriptors = $bindingDescriptors; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function postExecute() 52 | { 53 | foreach ($this->bindingDescriptors->toArray() as $bindingDescriptor) { 54 | if ($this->typeName === $bindingDescriptor->getTypeName()) { 55 | $this->reloadBindingDescriptor($bindingDescriptor); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Discovery/Binding/ReloadBindingDescriptorsByUuid.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Discovery\Type\BindingTypeDescriptorCollection; 15 | use Rhumsaa\Uuid\Uuid; 16 | 17 | /** 18 | * Reloads all binding descriptors with a given UUID. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class ReloadBindingDescriptorsByUuid extends AbstractReloadBindingDescriptors 25 | { 26 | /** 27 | * @var Uuid 28 | */ 29 | private $uuid; 30 | 31 | /** 32 | * @var BindingDescriptorCollection 33 | */ 34 | private $bindingDescriptors; 35 | 36 | public function __construct(Uuid $uuid, BindingDescriptorCollection $bindingDescriptors, BindingTypeDescriptorCollection $typeDescriptors) 37 | { 38 | parent::__construct($typeDescriptors); 39 | 40 | $this->uuid = $uuid; 41 | $this->bindingDescriptors = $bindingDescriptors; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function postExecute() 48 | { 49 | $this->reloadBindingDescriptor($this->bindingDescriptors->get($this->uuid)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Discovery/Binding/RemoveBindingDescriptorFromModuleFile.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Api\Discovery\BindingDescriptor; 15 | use Puli\Manager\Api\Module\RootModuleFile; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | use Rhumsaa\Uuid\Uuid; 18 | 19 | /** 20 | * Removes a binding descriptor from the root module file. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | class RemoveBindingDescriptorFromModuleFile implements AtomicOperation 27 | { 28 | /** 29 | * @var Uuid 30 | */ 31 | private $uuid; 32 | 33 | /** 34 | * @var RootModuleFile 35 | */ 36 | private $rootModuleFile; 37 | 38 | /** 39 | * @var BindingDescriptor 40 | */ 41 | private $previousDescriptor; 42 | 43 | public function __construct(Uuid $uuid, RootModuleFile $rootModuleFile) 44 | { 45 | $this->uuid = $uuid; 46 | $this->rootModuleFile = $rootModuleFile; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function execute() 53 | { 54 | if (!$this->rootModuleFile->hasBindingDescriptor($this->uuid)) { 55 | return; 56 | } 57 | 58 | $this->previousDescriptor = $this->rootModuleFile->getBindingDescriptor($this->uuid); 59 | $this->rootModuleFile->removeBindingDescriptor($this->uuid); 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function rollback() 66 | { 67 | if ($this->previousDescriptor) { 68 | $this->rootModuleFile->addBindingDescriptor($this->previousDescriptor); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Discovery/Binding/UnloadBindingDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Binding; 13 | 14 | use Puli\Manager\Api\Discovery\BindingDescriptor; 15 | use Puli\Manager\Api\Discovery\BindingTypeDescriptor; 16 | use Puli\Manager\Api\Module\Module; 17 | use Puli\Manager\Transaction\AtomicOperation; 18 | 19 | /** 20 | * Unloads a binding descriptor. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | class UnloadBindingDescriptor implements AtomicOperation 27 | { 28 | /** 29 | * @var BindingDescriptor 30 | */ 31 | private $bindingDescriptor; 32 | 33 | /** 34 | * @var BindingDescriptorCollection 35 | */ 36 | private $bindingDescriptors; 37 | 38 | /** 39 | * @var Module 40 | */ 41 | private $containingModule; 42 | 43 | /** 44 | * @var BindingTypeDescriptor 45 | */ 46 | private $typeDescriptor; 47 | 48 | /** 49 | * @var bool 50 | */ 51 | private $wasRemoved = false; 52 | 53 | public function __construct(BindingDescriptor $bindingDescriptor, BindingDescriptorCollection $bindingDescriptors) 54 | { 55 | $this->bindingDescriptor = $bindingDescriptor; 56 | $this->bindingDescriptors = $bindingDescriptors; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function execute() 63 | { 64 | // sanity check 65 | if (!$this->bindingDescriptor->isLoaded()) { 66 | return; 67 | } 68 | 69 | $this->containingModule = $this->bindingDescriptor->getContainingModule(); 70 | $this->typeDescriptor = $this->bindingDescriptor->getTypeDescriptor(); 71 | 72 | $uuid = $this->bindingDescriptor->getUuid(); 73 | 74 | // never fails with the check in the beginning 75 | $this->bindingDescriptor->unload(); 76 | 77 | if ($this->bindingDescriptors->contains($uuid) 78 | && $this->bindingDescriptor === $this->bindingDescriptors->get($uuid)) { 79 | // never fails 80 | $this->bindingDescriptors->remove($uuid); 81 | $this->wasRemoved = true; 82 | } 83 | } 84 | 85 | /** 86 | * {@inheritdoc} 87 | */ 88 | public function rollback() 89 | { 90 | if ($this->bindingDescriptor->isLoaded() || !$this->containingModule || !$this->typeDescriptor) { 91 | return; 92 | } 93 | 94 | // never fails with the check before, given that the type name of 95 | // the description/type didn't changed, which is impossible since 96 | // they're immutable 97 | $this->bindingDescriptor->load($this->containingModule, $this->typeDescriptor); 98 | 99 | if ($this->wasRemoved) { 100 | // never fails 101 | $this->bindingDescriptors->add($this->bindingDescriptor); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Discovery/Type/AddBindingType.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Type; 13 | 14 | use Puli\Discovery\Api\EditableDiscovery; 15 | use Puli\Manager\Api\Discovery\BindingTypeDescriptor; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Defines a type descriptor in the resource discovery. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class AddBindingType implements AtomicOperation 26 | { 27 | /** 28 | * @var BindingTypeDescriptor 29 | */ 30 | private $typeDescriptor; 31 | 32 | /** 33 | * @var EditableDiscovery 34 | */ 35 | private $discovery; 36 | 37 | public function __construct(BindingTypeDescriptor $typeDescriptor, EditableDiscovery $discovery) 38 | { 39 | $this->typeDescriptor = $typeDescriptor; 40 | $this->discovery = $discovery; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function execute() 47 | { 48 | $this->discovery->addBindingType($this->typeDescriptor->getType()); 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function rollback() 55 | { 56 | $this->discovery->removeBindingType($this->typeDescriptor->getTypeName()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Discovery/Type/AddTypeDescriptorToModuleFile.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Type; 13 | 14 | use Puli\Manager\Api\Discovery\BindingTypeDescriptor; 15 | use Puli\Manager\Api\Module\RootModuleFile; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Adds a type descriptor to the root module file. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class AddTypeDescriptorToModuleFile implements AtomicOperation 26 | { 27 | /** 28 | * @var BindingTypeDescriptor 29 | */ 30 | private $typeDescriptor; 31 | 32 | /** 33 | * @var RootModuleFile 34 | */ 35 | private $rootModuleFile; 36 | 37 | /** 38 | * @var BindingTypeDescriptor 39 | */ 40 | private $previousDescriptor; 41 | 42 | public function __construct(BindingTypeDescriptor $typeDescriptor, RootModuleFile $rootModuleFile) 43 | { 44 | $this->typeDescriptor = $typeDescriptor; 45 | $this->rootModuleFile = $rootModuleFile; 46 | } 47 | 48 | public function execute() 49 | { 50 | $typeName = $this->typeDescriptor->getTypeName(); 51 | 52 | if ($this->rootModuleFile->hasTypeDescriptor($typeName)) { 53 | $this->previousDescriptor = $this->rootModuleFile->getTypeDescriptor($typeName); 54 | } 55 | 56 | $this->rootModuleFile->addTypeDescriptor($this->typeDescriptor); 57 | } 58 | 59 | public function rollback() 60 | { 61 | if ($this->previousDescriptor) { 62 | $this->rootModuleFile->addTypeDescriptor($this->previousDescriptor); 63 | } else { 64 | $this->rootModuleFile->removeTypeDescriptor($this->typeDescriptor->getTypeName()); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Discovery/Type/LoadTypeDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Type; 13 | 14 | use Puli\Manager\Api\Discovery\BindingTypeDescriptor; 15 | use Puli\Manager\Api\Module\Module; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Loads a type descriptor. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class LoadTypeDescriptor implements AtomicOperation 26 | { 27 | /** 28 | * @var BindingTypeDescriptor 29 | */ 30 | private $typeDescriptor; 31 | 32 | /** 33 | * @var Module 34 | */ 35 | private $containingModule; 36 | 37 | /** 38 | * @var BindingTypeDescriptorCollection 39 | */ 40 | private $typeDescriptors; 41 | 42 | /** 43 | * @var BindingTypeDescriptor 44 | */ 45 | private $previousDescriptor; 46 | 47 | public function __construct(BindingTypeDescriptor $typeDescriptor, Module $containingModule, BindingTypeDescriptorCollection $types) 48 | { 49 | $this->typeDescriptor = $typeDescriptor; 50 | $this->containingModule = $containingModule; 51 | $this->typeDescriptors = $types; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function execute() 58 | { 59 | // sanity check 60 | if ($this->typeDescriptor->isLoaded()) { 61 | return; 62 | } 63 | 64 | // never fails with the check before 65 | $this->typeDescriptor->load($this->containingModule); 66 | 67 | $typeName = $this->typeDescriptor->getTypeName(); 68 | $moduleName = $this->containingModule->getName(); 69 | 70 | if ($this->typeDescriptors->contains($typeName, $moduleName)) { 71 | // never fails with the check before 72 | $this->previousDescriptor = $this->typeDescriptors->get($typeName, $moduleName); 73 | } 74 | 75 | // never fails 76 | $this->typeDescriptors->add($this->typeDescriptor); 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | public function rollback() 83 | { 84 | // sanity check 85 | if (!$this->typeDescriptor->isLoaded()) { 86 | return; 87 | } 88 | 89 | $typeName = $this->typeDescriptor->getTypeName(); 90 | 91 | // never fails with the check before 92 | $this->typeDescriptor->unload(); 93 | 94 | if ($this->previousDescriptor && $this->previousDescriptor->isLoaded()) { 95 | // never fails 96 | $this->typeDescriptors->add($this->previousDescriptor); 97 | } else { 98 | // never fails 99 | $this->typeDescriptors->remove($typeName, $this->containingModule->getName()); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Discovery/Type/RemoveTypeDescriptorFromModuleFile.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Type; 13 | 14 | use Puli\Manager\Api\Discovery\BindingTypeDescriptor; 15 | use Puli\Manager\Api\Module\RootModuleFile; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Removes a type descriptor from the root module file. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class RemoveTypeDescriptorFromModuleFile implements AtomicOperation 26 | { 27 | /** 28 | * @var BindingTypeDescriptor 29 | */ 30 | private $typeName; 31 | 32 | /** 33 | * @var RootModuleFile 34 | */ 35 | private $rootModuleFile; 36 | 37 | /** 38 | * @var BindingTypeDescriptor 39 | */ 40 | private $previousDescriptor; 41 | 42 | public function __construct($typeName, RootModuleFile $rootModuleFile) 43 | { 44 | $this->typeName = $typeName; 45 | $this->rootModuleFile = $rootModuleFile; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function execute() 52 | { 53 | if (!$this->rootModuleFile->hasTypeDescriptor($this->typeName)) { 54 | return; 55 | } 56 | 57 | $this->previousDescriptor = $this->rootModuleFile->getTypeDescriptor($this->typeName); 58 | $this->rootModuleFile->removeTypeDescriptor($this->typeName); 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function rollback() 65 | { 66 | if ($this->previousDescriptor) { 67 | $this->rootModuleFile->addTypeDescriptor($this->previousDescriptor); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Discovery/Type/UnloadTypeDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Type; 13 | 14 | use Puli\Manager\Api\Discovery\BindingTypeDescriptor; 15 | use Puli\Manager\Api\Module\Module; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Unloads a type descriptor. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class UnloadTypeDescriptor implements AtomicOperation 26 | { 27 | /** 28 | * @var BindingTypeDescriptor 29 | */ 30 | private $typeDescriptor; 31 | 32 | /** 33 | * @var BindingTypeDescriptorCollection 34 | */ 35 | private $typeDescriptors; 36 | 37 | /** 38 | * @var Module 39 | */ 40 | private $containingModule; 41 | 42 | /** 43 | * @var bool 44 | */ 45 | private $wasRemoved = false; 46 | 47 | public function __construct($typeDescriptor, BindingTypeDescriptorCollection $typeDescriptors) 48 | { 49 | $this->typeDescriptor = $typeDescriptor; 50 | $this->typeDescriptors = $typeDescriptors; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function execute() 57 | { 58 | // sanity check 59 | if (!$this->typeDescriptor->isLoaded()) { 60 | return; 61 | } 62 | 63 | // never fails with the check before 64 | $this->containingModule = $this->typeDescriptor->getContainingModule(); 65 | 66 | $typeName = $this->typeDescriptor->getTypeName(); 67 | $moduleName = $this->containingModule->getName(); 68 | 69 | // never fails with the check before 70 | $this->typeDescriptor->unload(); 71 | 72 | if ($this->typeDescriptors->contains($typeName, $moduleName) 73 | && $this->typeDescriptor === $this->typeDescriptors->get($typeName, $moduleName)) { 74 | // never fails 75 | $this->typeDescriptors->remove($typeName, $moduleName); 76 | $this->wasRemoved = true; 77 | } 78 | } 79 | 80 | /** 81 | * {@inheritdoc} 82 | */ 83 | public function rollback() 84 | { 85 | // sanity check 86 | if ($this->typeDescriptor->isLoaded() || !$this->containingModule) { 87 | return; 88 | } 89 | 90 | // never fails with the check before 91 | $this->typeDescriptor->load($this->containingModule); 92 | 93 | if ($this->wasRemoved) { 94 | // never fails 95 | $this->typeDescriptors->add($this->typeDescriptor); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Discovery/Type/UpdateDuplicateMarksForTypeName.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Discovery\Type; 13 | 14 | use Puli\Manager\Transaction\OperationInterceptor; 15 | use Rhumsaa\Uuid\Uuid; 16 | 17 | /** 18 | * Updates the duplicate marks of all types with the given type name. 19 | * 20 | * If more than one type is defined for the given type name, all types are 21 | * marked as duplicates. Otherwise the single type is marked as no duplicate. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class UpdateDuplicateMarksForTypeName implements OperationInterceptor 28 | { 29 | /** 30 | * @var Uuid 31 | */ 32 | private $typeName; 33 | 34 | /** 35 | * @var BindingTypeDescriptorCollection 36 | */ 37 | private $typeDescriptors; 38 | 39 | public function __construct($typeName, BindingTypeDescriptorCollection $typeDescriptors) 40 | { 41 | $this->typeName = $typeName; 42 | $this->typeDescriptors = $typeDescriptors; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function postExecute() 49 | { 50 | $this->updateDuplicateMarksForTypeName($this->typeName); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function postRollback() 57 | { 58 | $this->updateDuplicateMarksForTypeName($this->typeName); 59 | } 60 | 61 | private function updateDuplicateMarksForTypeName($typeName) 62 | { 63 | if (!$this->typeDescriptors->contains($typeName)) { 64 | return; 65 | } 66 | 67 | $typeDescriptors = $this->typeDescriptors->listByTypeName($typeName); 68 | $duplicate = count($typeDescriptors) > 1; 69 | 70 | foreach ($typeDescriptors as $typeDescriptor) { 71 | $typeDescriptor->markDuplicate($duplicate); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Factory/Generator/ChangeStream/JsonChangeStreamGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\ChangeStream; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * Generates the setup code for a {@link JsonChangeStream}. 23 | * 24 | * @since 1.0 25 | * 26 | * @author Bernhard Schussek 27 | */ 28 | class JsonChangeStreamGenerator implements ServiceGenerator 29 | { 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 34 | { 35 | Assert::keyExists($options, 'root-dir', 'The "root-dir" option is missing.'); 36 | 37 | if (!isset($options['path'])) { 38 | $options['path'] = $targetMethod->getClass()->getDirectory().'/change-stream.json'; 39 | } 40 | 41 | Assert::stringNotEmpty($options['root-dir'], 'The "root-dir" option should be a non-empty string. Got: %s'); 42 | Assert::stringNotEmpty($options['path'], 'The "path" option should be a non-empty string. Got: %s'); 43 | 44 | $path = Path::makeAbsolute($options['path'], $options['root-dir']); 45 | $relPath = Path::makeRelative($path, $targetMethod->getClass()->getDirectory()); 46 | 47 | $escPath = '__DIR__.'.var_export('/'.$relPath, true); 48 | 49 | $targetMethod->getClass()->addImport(new Import('Puli\Repository\ChangeStream\JsonChangeStream')); 50 | 51 | $targetMethod->addBody(sprintf( 52 | '$%s = new JsonChangeStream(%s);', 53 | $varName, 54 | $escPath 55 | )); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Factory/Generator/ChangeStream/KeyValueStoreChangeStreamGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\ChangeStream; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | 20 | /** 21 | * Generates the setup code for a {@link KeyValueStoreChangeStream}. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class KeyValueStoreChangeStreamGenerator implements ServiceGenerator 28 | { 29 | private static $defaultOptions = array( 30 | 'store' => array( 31 | 'type' => 'json', 32 | ), 33 | ); 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 39 | { 40 | Assert::keyExists($options, 'root-dir', 'The "root-dir" option is missing.'); 41 | 42 | $options = array_replace_recursive(self::$defaultOptions, $options); 43 | 44 | Assert::stringNotEmpty($options['root-dir'], 'The "root-dir" option should be a non-empty string. Got: %s'); 45 | Assert::isArray($options['store'], 'The "store" option should be an array. Got: %s'); 46 | 47 | if (!isset($options['store']['path'])) { 48 | $options['store']['path'] = $targetMethod->getClass()->getDirectory().'/change-stream.json'; 49 | } 50 | 51 | $kvsGenerator = $generatorRegistry->getServiceGenerator(GeneratorRegistry::KEY_VALUE_STORE, $options['store']['type']); 52 | $kvsOptions = $options['store']; 53 | $kvsOptions['root-dir'] = $options['root-dir']; 54 | $kvsGenerator->generateNewInstance('store', $targetMethod, $generatorRegistry, $kvsOptions); 55 | 56 | $targetMethod->getClass()->addImport(new Import('Puli\Repository\ChangeStream\KeyValueStoreChangeStream')); 57 | 58 | $targetMethod->addBody(sprintf( 59 | '$%s = new KeyValueStoreChangeStream($store);', 60 | $varName 61 | )); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Factory/Generator/DefaultGeneratorRegistry.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use RuntimeException; 16 | 17 | /** 18 | * Puli's default {@link GeneratorRegistry}. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class DefaultGeneratorRegistry implements GeneratorRegistry 25 | { 26 | /** 27 | * @var string[][] 28 | */ 29 | private static $classNames = array( 30 | self::REPOSITORY => array( 31 | 'filesystem' => 'Puli\Manager\Factory\Generator\Repository\FilesystemRepositoryGenerator', 32 | 'json' => 'Puli\Manager\Factory\Generator\Repository\JsonRepositoryGenerator', 33 | // for BC 34 | 'path-mapping' => 'Puli\Manager\Factory\Generator\Repository\JsonRepositoryGenerator', 35 | ), 36 | self::DISCOVERY => array( 37 | 'json' => 'Puli\Manager\Factory\Generator\Discovery\JsonDiscoveryGenerator', 38 | 'key-value-store' => 'Puli\Manager\Factory\Generator\Discovery\KeyValueStoreDiscoveryGenerator', 39 | ), 40 | self::KEY_VALUE_STORE => array( 41 | null => 'Puli\Manager\Factory\Generator\KeyValueStore\NullStoreGenerator', 42 | 'null' => 'Puli\Manager\Factory\Generator\KeyValueStore\NullStoreGenerator', 43 | 'array' => 'Puli\Manager\Factory\Generator\KeyValueStore\ArrayStoreGenerator', 44 | 'json' => 'Puli\Manager\Factory\Generator\KeyValueStore\JsonFileStoreGenerator', 45 | // for BC 46 | 'json-file' => 'Puli\Manager\Factory\Generator\KeyValueStore\JsonFileStoreGenerator', 47 | 'php-redis' => 'Puli\Manager\Factory\Generator\KeyValueStore\PhpRedisStoreGenerator', 48 | 'predis' => 'Puli\Manager\Factory\Generator\KeyValueStore\PredisStoreGenerator', 49 | 'riak' => 'Puli\Manager\Factory\Generator\KeyValueStore\RiakStoreGenerator', 50 | ), 51 | self::CHANGE_STREAM => array( 52 | 'json' => 'Puli\Manager\Factory\Generator\ChangeStream\JsonChangeStreamGenerator', 53 | 'key-value-store' => 'Puli\Manager\Factory\Generator\ChangeStream\KeyValueStoreChangeStreamGenerator', 54 | ), 55 | ); 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function getServiceGenerator($type, $name) 61 | { 62 | if (!isset(self::$classNames[$type])) { 63 | throw new RuntimeException(sprintf( 64 | 'The service type "%s" is not supported.', 65 | $type 66 | )); 67 | } 68 | 69 | if (!isset(self::$classNames[$type][$name])) { 70 | throw new RuntimeException(sprintf( 71 | 'The service "%s" of type "%s" does not exist.', 72 | $name, 73 | $type 74 | )); 75 | } 76 | 77 | $className = self::$classNames[$type][$name]; 78 | 79 | return new $className(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Factory/Generator/Discovery/JsonDiscoveryGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\Discovery; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * Generates the setup code for a {@link JsonDiscovery}. 23 | * 24 | * @since 1.0 25 | * 26 | * @author Bernhard Schussek 27 | */ 28 | class JsonDiscoveryGenerator implements ServiceGenerator 29 | { 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 34 | { 35 | Assert::keyExists($options, 'root-dir', 'The "root-dir" option is missing.'); 36 | 37 | if (!isset($options['path'])) { 38 | $options['path'] = $targetMethod->getClass()->getDirectory().'/bindings.json'; 39 | } 40 | 41 | Assert::stringNotEmpty($options['root-dir'], 'The "root-dir" option should be a non-empty string. Got: %s'); 42 | Assert::stringNotEmpty($options['path'], 'The "path" option should be a non-empty string. Got: %s'); 43 | 44 | $path = Path::makeAbsolute($options['path'], $options['root-dir']); 45 | $relPath = Path::makeRelative($path, $targetMethod->getClass()->getDirectory()); 46 | 47 | $escPath = '__DIR__.'.var_export('/'.$relPath, true); 48 | 49 | $targetMethod->getClass()->addImport(new Import('Puli\Discovery\JsonDiscovery')); 50 | $targetMethod->getClass()->addImport(new Import('Puli\Discovery\Binding\Initializer\ResourceBindingInitializer')); 51 | 52 | $targetMethod->addBody(sprintf( 53 | "$%s = new JsonDiscovery(%s, array(\n new ResourceBindingInitializer(\$repo),\n));", 54 | $varName, 55 | $escPath 56 | )); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Factory/Generator/Discovery/KeyValueStoreDiscoveryGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\Discovery; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | 20 | /** 21 | * Generates the setup code for a {@link KeyValueStoreDiscovery}. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class KeyValueStoreDiscoveryGenerator implements ServiceGenerator 28 | { 29 | private static $defaultOptions = array( 30 | 'store' => array( 31 | 'type' => 'json', 32 | ), 33 | ); 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 39 | { 40 | Assert::keyExists($options, 'root-dir', 'The "root-dir" option is missing.'); 41 | 42 | $options = array_replace_recursive(self::$defaultOptions, $options); 43 | 44 | Assert::stringNotEmpty($options['root-dir'], 'The "root-dir" option should be a non-empty string. Got: %s'); 45 | Assert::isArray($options['store'], 'The "store" option should be an array. Got: %s'); 46 | 47 | if (!isset($options['store']['path'])) { 48 | $options['store']['path'] = $targetMethod->getClass()->getDirectory().'/bindings.json'; 49 | } 50 | 51 | $kvsGenerator = $generatorRegistry->getServiceGenerator(GeneratorRegistry::KEY_VALUE_STORE, $options['store']['type']); 52 | $kvsOptions = $options['store']; 53 | $kvsOptions['root-dir'] = $options['root-dir']; 54 | $kvsGenerator->generateNewInstance('store', $targetMethod, $generatorRegistry, $kvsOptions); 55 | 56 | $targetMethod->getClass()->addImport(new Import('Puli\Discovery\KeyValueStoreDiscovery')); 57 | $targetMethod->getClass()->addImport(new Import('Puli\Discovery\Binding\Initializer\ResourceBindingInitializer')); 58 | 59 | $targetMethod->addBody(sprintf( 60 | "$%s = new KeyValueStoreDiscovery(\$store, array(\n new ResourceBindingInitializer(\$repo),\n));", 61 | $varName 62 | )); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Factory/Generator/KeyValueStore/ArrayStoreGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\KeyValueStore; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | 19 | /** 20 | * Generates the setup code for an {@link ArrayStore}. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | class ArrayStoreGenerator implements ServiceGenerator 27 | { 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 32 | { 33 | $targetMethod->getClass()->addImport(new Import('Webmozart\KeyValueStore\ArrayStore')); 34 | 35 | $targetMethod->addBody(sprintf( 36 | '$%s = new ArrayStore();', 37 | $varName 38 | )); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Factory/Generator/KeyValueStore/JsonFileStoreGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\KeyValueStore; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * Generates the setup code for a {@link JsonFileStore}. 23 | * 24 | * @since 1.0 25 | * 26 | * @author Bernhard Schussek 27 | */ 28 | class JsonFileStoreGenerator implements ServiceGenerator 29 | { 30 | private static $defaultOptions = array( 31 | 'path' => 'data.json', 32 | 'serialize-strings' => true, 33 | 'serialize-arrays' => true, 34 | 'escape-slash' => true, 35 | 'pretty-print' => false, 36 | ); 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 42 | { 43 | Assert::keyExists($options, 'root-dir', 'The "root-dir" option is missing.'); 44 | 45 | $options = array_replace(self::$defaultOptions, $options); 46 | 47 | Assert::stringNotEmpty($options['path'], 'The "path" option should be a non-empty string. Got: %s'); 48 | Assert::stringNotEmpty($options['root-dir'], 'The "root-dir" option should be a non-empty string. Got: %s'); 49 | Assert::boolean($options['serialize-strings'], 'The "serialize-strings" option should be a boolean. Got: %s'); 50 | Assert::boolean($options['serialize-arrays'], 'The "serialize-arrays" option should be a boolean. Got: %s'); 51 | Assert::boolean($options['escape-slash'], 'The "escape-slash" option should be a boolean. Got: %s'); 52 | Assert::boolean($options['pretty-print'], 'The "pretty-print" option should be a boolean. Got: %s'); 53 | 54 | $path = Path::makeAbsolute($options['path'], $options['root-dir']); 55 | $relPath = Path::makeRelative($path, $targetMethod->getClass()->getDirectory()); 56 | 57 | $flags = array(); 58 | 59 | if (!$options['serialize-strings']) { 60 | $flags[] = 'JsonFileStore::NO_SERIALIZE_STRINGS'; 61 | } 62 | 63 | if (!$options['serialize-arrays']) { 64 | $flags[] = 'JsonFileStore::NO_SERIALIZE_ARRAYS'; 65 | } 66 | 67 | if (!$options['serialize-arrays']) { 68 | $flags[] = 'JsonFileStore::NO_ESCAPE_SLASH'; 69 | } 70 | 71 | if ($options['pretty-print']) { 72 | $flags[] = 'JsonFileStore::PRETTY_PRINT'; 73 | } 74 | 75 | $targetMethod->getClass()->addImport(new Import('Webmozart\KeyValueStore\JsonFileStore')); 76 | 77 | $targetMethod->addBody(sprintf('$%s = new JsonFileStore(%s%s%s);', 78 | $varName, 79 | $flags ? "\n " : '', 80 | '__DIR__.'.var_export('/'.$relPath, true), 81 | $flags ? ",\n ".implode("\n | ", $flags)."\n" : '' 82 | )); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Factory/Generator/KeyValueStore/NullStoreGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\KeyValueStore; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | 19 | /** 20 | * Generates the setup code for a {@link NullStore}. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | class NullStoreGenerator implements ServiceGenerator 27 | { 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 32 | { 33 | $targetMethod->getClass()->addImport(new Import('Webmozart\KeyValueStore\NullStore')); 34 | 35 | $targetMethod->addBody(sprintf( 36 | '$%s = new NullStore();', 37 | $varName 38 | )); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Factory/Generator/KeyValueStore/PhpRedisStoreGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\KeyValueStore; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | 20 | /** 21 | * Generates the setup code for a {@link PhpRedisStore}. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class PhpRedisStoreGenerator implements ServiceGenerator 28 | { 29 | private static $defaultOptions = array( 30 | 'host' => '127.0.0.1', 31 | 'port' => 6379, 32 | ); 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 38 | { 39 | $options = array_replace(self::$defaultOptions, $options); 40 | 41 | Assert::stringNotEmpty($options['host'], 'The "host" option must be a non-empty string. Got: %s'); 42 | Assert::integer($options['port'], 'The "port" option must be an integer. Got: %s'); 43 | 44 | $escHost = var_export($options['host'], true); 45 | $escPort = var_export($options['port'], true); 46 | 47 | $targetMethod->getClass()->addImports(array( 48 | new Import('Redis'), 49 | new Import('Webmozart\KeyValueStore\PhpRedisStore'), 50 | )); 51 | 52 | $targetMethod->addBody( 53 | <<connect($escHost, $escPort); 56 | \$$varName = new PhpRedisStore(\$client); 57 | EOF 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Factory/Generator/KeyValueStore/PredisStoreGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\KeyValueStore; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | 20 | /** 21 | * Generates the setup code for a {@link PredisStore}. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class PredisStoreGenerator implements ServiceGenerator 28 | { 29 | private static $defaultOptions = array( 30 | 'host' => '127.0.0.1', 31 | 'port' => 6379, 32 | ); 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 38 | { 39 | $options = array_replace(self::$defaultOptions, $options); 40 | 41 | Assert::stringNotEmpty($options['host'], 'The "host" option must be a non-empty string. Got: %s'); 42 | Assert::integer($options['port'], 'The "port" option must be an integer. Got: %s'); 43 | 44 | $escHost = var_export($options['host'], true); 45 | $escPort = var_export($options['port'], true); 46 | 47 | $targetMethod->getClass()->addImports(array( 48 | new Import('Predis\Client'), 49 | new Import('Webmozart\KeyValueStore\PredisStore'), 50 | )); 51 | 52 | $targetMethod->addBody( 53 | << $escHost, 56 | 'port' => $escPort, 57 | )); 58 | \$$varName = new PredisStore(\$client); 59 | EOF 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Factory/Generator/KeyValueStore/RiakStoreGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\KeyValueStore; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | 20 | /** 21 | * Generates the setup code for a {@link RiakStore}. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class RiakStoreGenerator implements ServiceGenerator 28 | { 29 | private static $defaultOptions = array( 30 | 'host' => '127.0.0.1', 31 | 'port' => 8098, 32 | ); 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 38 | { 39 | Assert::keyExists($options, 'bucket', 'The "bucket" option is missing.'); 40 | 41 | $options = array_replace(self::$defaultOptions, $options); 42 | 43 | Assert::stringNotEmpty($options['bucket'], 'The "bucket" option must be a non-empty string. Got: %s'); 44 | Assert::stringNotEmpty($options['host'], 'The "host" option must be a non-empty string. Got: %s'); 45 | Assert::integer($options['port'], 'The "port" option must be an integer. Got: %s'); 46 | 47 | $escBucket = var_export($options['bucket'], true); 48 | $escHost = var_export($options['host'], true); 49 | $escPort = var_export($options['port'], true); 50 | 51 | $targetMethod->getClass()->addImports(array( 52 | new Import('Basho\Riak\Riak'), 53 | new Import('Webmozart\KeyValueStore\RiakStore'), 54 | )); 55 | 56 | $targetMethod->addBody( 57 | << 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\Repository; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * Generates the setup code for a {@link FilesystemRepository}. 23 | * 24 | * @since 1.0 25 | * 26 | * @author Bernhard Schussek 27 | */ 28 | class FilesystemRepositoryGenerator implements ServiceGenerator 29 | { 30 | private static $defaultOptions = array( 31 | 'symlink' => true, 32 | ); 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 38 | { 39 | Assert::keyExists($options, 'root-dir', 'The "root-dir" option is missing.'); 40 | 41 | $options = array_replace(self::$defaultOptions, $options); 42 | 43 | if (!isset($options['path'])) { 44 | $options['path'] = $targetMethod->getClass()->getDirectory().'/repository'; 45 | } 46 | 47 | Assert::stringNotEmpty($options['path'], 'The "path" option should be a non-empty string. Got: %s'); 48 | Assert::stringNotEmpty($options['root-dir'], 'The "root-dir" option should be a non-empty string. Got: %s'); 49 | Assert::boolean($options['symlink'], 'The "symlink" option should be a boolean. Got: %s'); 50 | 51 | $path = Path::makeAbsolute($options['path'], $options['root-dir']); 52 | $relPath = Path::makeRelative($path, $targetMethod->getClass()->getDirectory()); 53 | 54 | $escPath = $relPath 55 | ? '__DIR__.'.var_export('/'.$relPath, true) 56 | : '__DIR__'; 57 | 58 | if ($relPath) { 59 | $targetMethod->addBody( 60 | <<getClass()->addImport(new Import('Puli\Repository\FilesystemRepository')); 70 | 71 | $targetMethod->addBody(sprintf( 72 | '$%s = new FilesystemRepository(%s, %s);', 73 | $varName, 74 | $escPath, 75 | var_export($options['symlink'], true) 76 | )); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Factory/Generator/Repository/JsonRepositoryGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Factory\Generator\Repository; 13 | 14 | use Puli\Manager\Api\Factory\Generator\GeneratorRegistry; 15 | use Puli\Manager\Api\Factory\Generator\ServiceGenerator; 16 | use Puli\Manager\Api\Php\Import; 17 | use Puli\Manager\Api\Php\Method; 18 | use Puli\Manager\Assert\Assert; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * Generates the setup code for a {@link JsonRepository}. 23 | * 24 | * @since 1.0 25 | * 26 | * @author Bernhard Schussek 27 | */ 28 | class JsonRepositoryGenerator implements ServiceGenerator 29 | { 30 | private static $defaultOptions = array( 31 | 'optimize' => false, 32 | 'change-stream' => array( 33 | 'type' => 'json', 34 | ), 35 | ); 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function generateNewInstance($varName, Method $targetMethod, GeneratorRegistry $generatorRegistry, array $options = array()) 41 | { 42 | Assert::keyExists($options, 'root-dir', 'The "root-dir" option is missing.'); 43 | 44 | $options = array_replace_recursive(self::$defaultOptions, $options); 45 | 46 | if (!isset($options['path'])) { 47 | $options['path'] = $targetMethod->getClass()->getDirectory().'/path-mappings.json'; 48 | } 49 | 50 | Assert::stringNotEmpty($options['path'], 'The "path" option should be a non-empty string. Got: %s'); 51 | Assert::stringNotEmpty($options['root-dir'], 'The "root-dir" option should be a non-empty string. Got: %s'); 52 | Assert::boolean($options['optimize'], 'The "optimize" option should be a boolean. Got: %s'); 53 | Assert::isArray($options['change-stream'], 'The "change-stream" option should be an array. Got: %s'); 54 | 55 | $path = Path::makeAbsolute($options['path'], $options['root-dir']); 56 | $relPath = Path::makeRelative($path, $targetMethod->getClass()->getDirectory()); 57 | $relBaseDir = Path::makeRelative($options['root-dir'], $targetMethod->getClass()->getDirectory()); 58 | 59 | $escPath = '__DIR__.'.var_export('/'.$relPath, true); 60 | $escBaseDir = $relBaseDir 61 | ? '__DIR__.'.var_export('/'.$relBaseDir, true) 62 | : '__DIR__'; 63 | 64 | if ($options['optimize']) { 65 | $streamGenerator = $generatorRegistry->getServiceGenerator(GeneratorRegistry::CHANGE_STREAM, $options['change-stream']['type']); 66 | $streamOptions = $options['change-stream']; 67 | $streamOptions['root-dir'] = $options['root-dir']; 68 | $streamGenerator->generateNewInstance('stream', $targetMethod, $generatorRegistry, $streamOptions); 69 | 70 | $targetMethod->getClass()->addImport(new Import('Puli\\Repository\\OptimizedJsonRepository')); 71 | 72 | $targetMethod->addBody(sprintf( 73 | // Deactivate schema validation for performance 74 | '$%s = new OptimizedJsonRepository(%s, %s, false, $stream);', 75 | $varName, 76 | $escPath, 77 | $escBaseDir 78 | )); 79 | } else { 80 | $targetMethod->getClass()->addImport(new Import('Puli\\Repository\\JsonRepository')); 81 | 82 | $targetMethod->addBody(sprintf( 83 | // Activate schema validation 84 | '$%s = new JsonRepository(%s, %s, true);', 85 | $varName, 86 | $escPath, 87 | $escBaseDir 88 | )); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Filesystem/FilesystemStorage.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Filesystem; 13 | 14 | use Puli\Manager\Api\FileNotFoundException; 15 | use Puli\Manager\Api\Storage\ReadException; 16 | use Puli\Manager\Api\Storage\Storage; 17 | use Puli\Manager\Assert\Assert; 18 | use Symfony\Component\Filesystem\Filesystem; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * Stores files on the filesystem. 23 | * 24 | * @since 1.0 25 | * 26 | * @author Bernhard Schussek 27 | */ 28 | class FilesystemStorage implements Storage 29 | { 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function read($path) 34 | { 35 | Assert::notEmpty($path, 'Cannot read an empty path.'); 36 | 37 | if (!file_exists($path)) { 38 | throw new FileNotFoundException(sprintf('Cannot read %s: File not found.', $path)); 39 | } 40 | 41 | if (is_dir($path)) { 42 | throw new ReadException(sprintf('Cannot read %s: Is a directory.', $path)); 43 | } 44 | 45 | if (false === ($contents = @file_get_contents($path))) { 46 | $error = error_get_last(); 47 | 48 | throw new ReadException(sprintf('Could not read %s: %s.', $path, $error['message'])); 49 | } 50 | 51 | return $contents; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function write($path, $contents) 58 | { 59 | Assert::notEmpty($path, 'Cannot write to an empty path.'); 60 | 61 | if (is_dir($path)) { 62 | throw new ReadException(sprintf('Cannot write %s: Is a directory.', $path)); 63 | } 64 | 65 | if (!is_dir($dir = Path::getDirectory($path))) { 66 | $filesystem = new Filesystem(); 67 | $filesystem->mkdir($dir); 68 | } 69 | 70 | if (false === ($numBytes = @file_put_contents($path, $contents))) { 71 | $error = error_get_last(); 72 | 73 | throw new ReadException(sprintf('Could not write %s: %s.', $path, $error['message'])); 74 | } 75 | 76 | return $numBytes; 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | public function exists($path) 83 | { 84 | return file_exists($path); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Installer/CopyInstaller.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Installer; 13 | 14 | use Puli\Manager\Api\Installation\InstallationParams; 15 | use Puli\Manager\Api\Installer\ResourceInstaller; 16 | use Puli\Repository\Api\Resource\FilesystemResource; 17 | use Puli\Repository\Api\Resource\PuliResource; 18 | use Puli\Repository\FilesystemRepository; 19 | use Webmozart\PathUtil\Path; 20 | 21 | /** 22 | * Installs resources via a local filesystem copy. 23 | * 24 | * @since 1.0 25 | * 26 | * @author Bernhard Schussek 27 | */ 28 | class CopyInstaller implements ResourceInstaller 29 | { 30 | /** 31 | * @var bool 32 | */ 33 | protected $symlinks = false; 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function validateParams(InstallationParams $params) 39 | { 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function installResource(PuliResource $resource, InstallationParams $params) 46 | { 47 | $documentRoot = Path::makeAbsolute($params->getDocumentRoot(), $params->getRootDirectory()); 48 | 49 | if (!file_exists($documentRoot)) { 50 | mkdir($documentRoot, 0777, true); 51 | } 52 | 53 | $serverPath = $params->getServerPathForResource($resource); 54 | $parameterValues = $params->getParameterValues(); 55 | $relative = !isset($parameterValues['relative']) || $parameterValues['relative']; 56 | $filesystemRepo = new FilesystemRepository($documentRoot, $this->symlinks, $relative); 57 | 58 | if ('/' === $serverPath) { 59 | foreach ($resource->listChildren() as $child) { 60 | $name = $child->getName(); 61 | 62 | // If the resource is not attached, the name is empty 63 | if (!$name && $child instanceof FilesystemResource) { 64 | $name = Path::getFilename($child->getFilesystemPath()); 65 | } 66 | 67 | if ($name) { 68 | $filesystemRepo->remove($serverPath.'/'.$name); 69 | } 70 | } 71 | } else { 72 | $filesystemRepo->remove($serverPath); 73 | } 74 | 75 | // Don't attach the original resource to the repository we just created 76 | $filesystemRepo->add($serverPath, clone $resource); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Installer/SymlinkInstaller.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Installer; 13 | 14 | /** 15 | * Installs resources via symbolic links on the local filesystem. 16 | * 17 | * @since 1.0 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | class SymlinkInstaller extends CopyInstaller 22 | { 23 | /** 24 | * @var bool 25 | */ 26 | protected $symlinks = true; 27 | } 28 | -------------------------------------------------------------------------------- /src/Json/ChainVersioner.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Json; 13 | 14 | use stdClass; 15 | use Webmozart\Json\Versioning\CannotParseVersionException; 16 | use Webmozart\Json\Versioning\CannotUpdateVersionException; 17 | use Webmozart\Json\Versioning\JsonVersioner; 18 | 19 | /** 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class ChainVersioner implements JsonVersioner 25 | { 26 | /** 27 | * @var JsonVersioner[] 28 | */ 29 | private $versioners; 30 | 31 | /** 32 | * @param JsonVersioner[] $versioners 33 | */ 34 | public function __construct(array $versioners) 35 | { 36 | $this->versioners = $versioners; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function parseVersion(stdClass $jsonData) 43 | { 44 | /** @var CannotParseVersionException $firstException */ 45 | $firstException = null; 46 | 47 | foreach ($this->versioners as $versioner) { 48 | try { 49 | return $versioner->parseVersion($jsonData); 50 | } catch (CannotParseVersionException $e) { 51 | if (null === $firstException) { 52 | $firstException = $e; 53 | } 54 | } 55 | } 56 | 57 | throw $firstException; 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public function updateVersion(stdClass $jsonData, $version) 64 | { 65 | /** @var CannotUpdateVersionException $firstException */ 66 | $firstException = null; 67 | 68 | foreach ($this->versioners as $versioner) { 69 | try { 70 | $versioner->updateVersion($jsonData, $version); 71 | 72 | return; 73 | } catch (CannotUpdateVersionException $e) { 74 | if (null === $firstException) { 75 | $firstException = $e; 76 | } 77 | } 78 | } 79 | 80 | throw $firstException; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Json/JsonConverterProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Json; 13 | 14 | use InvalidArgumentException; 15 | use Puli\Manager\Api\Container; 16 | use Webmozart\Json\Conversion\JsonConverter; 17 | 18 | /** 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class JsonConverterProvider 24 | { 25 | /** 26 | * @var Container 27 | */ 28 | private $container; 29 | 30 | public function __construct(Container $container) 31 | { 32 | $this->container = $container; 33 | } 34 | 35 | /** 36 | * @param string $className The name of the class to convert. 37 | * 38 | * @return JsonConverter The JSON converter. 39 | */ 40 | public function getJsonConverter($className) 41 | { 42 | switch ($className) { 43 | case 'Puli\Manager\Api\Config\ConfigFile': 44 | return $this->container->getConfigFileConverter(); 45 | 46 | case 'Puli\Manager\Api\Module\ModuleFile': 47 | return $this->container->getLegacyModuleFileConverter(); 48 | 49 | case 'Puli\Manager\Api\Module\RootModuleFile': 50 | return $this->container->getLegacyRootModuleFileConverter(); 51 | 52 | default: 53 | throw new InvalidArgumentException(sprintf( 54 | 'Could not find converter for class "%s".', 55 | $className 56 | )); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Json/LocalUriRetriever.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Json; 13 | 14 | use JsonSchema\Uri\Retrievers\FileGetContents; 15 | use Webmozart\PathUtil\Path; 16 | 17 | /** 18 | * @since 1.0 19 | * 20 | * @author Bernhard Schussek 21 | */ 22 | class LocalUriRetriever extends FileGetContents 23 | { 24 | /** 25 | * @var string 26 | */ 27 | private $baseDir; 28 | 29 | public function __construct() 30 | { 31 | $this->baseDir = 'file://'.Path::canonicalize(__DIR__.'/../../res/schema'); 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function retrieve($uri) 38 | { 39 | switch ($uri) { 40 | case 'http://puli.io/schema/1.0/manager/module': 41 | $uri = $this->baseDir.'/module-schema-1.0.json'; 42 | break; 43 | 44 | case 'http://puli.io/schema/2.0/manager/module': 45 | $uri = $this->baseDir.'/module-schema-2.0.json'; 46 | break; 47 | 48 | case 'http://puli.io/schema/1.0/manager/dependencies': 49 | $uri = $this->baseDir.'/dependencies-schema-1.0.json'; 50 | break; 51 | 52 | default: 53 | break; 54 | } 55 | 56 | return parent::retrieve($uri); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Module/Migration/ModuleFile10To20Migration.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Module\Migration; 13 | 14 | use Puli\Manager\Module\ModuleFileConverter; 15 | use stdClass; 16 | use Webmozart\Json\Migration\JsonMigration; 17 | 18 | /** 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | */ 23 | class ModuleFile10To20Migration implements JsonMigration 24 | { 25 | public function getSourceVersion() 26 | { 27 | return '1.0'; 28 | } 29 | 30 | public function getTargetVersion() 31 | { 32 | return '2.0'; 33 | } 34 | 35 | public function up(stdClass $data) 36 | { 37 | $data->{'$schema'} = sprintf(ModuleFileConverter::SCHEMA, '2.0'); 38 | 39 | unset($data->version); 40 | 41 | if (isset($data->packages)) { 42 | $data->modules = $data->packages; 43 | unset($data->packages); 44 | } 45 | 46 | if (isset($data->{'path-mappings'})) { 47 | $data->resources = $data->{'path-mappings'}; 48 | unset($data->{'path-mappings'}); 49 | } 50 | 51 | if (isset($data->override) && !is_array($data->override)) { 52 | $data->override = array($data->override); 53 | } 54 | 55 | if (isset($data->{'override-order'})) { 56 | $data->order = $data->{'override-order'}; 57 | unset($data->{'override-order'}); 58 | } 59 | 60 | if (isset($data->override)) { 61 | $data->depend = $data->override; 62 | unset($data->override); 63 | } 64 | } 65 | 66 | public function down(stdClass $data) 67 | { 68 | unset($data->{'$schema'}); 69 | 70 | $data->version = '1.0'; 71 | 72 | if (isset($data->modules)) { 73 | $data->packages = $data->modules; 74 | unset($data->modules); 75 | } 76 | 77 | if (isset($data->resources)) { 78 | $data->{'path-mappings'} = $data->resources; 79 | unset($data->resources); 80 | } 81 | 82 | if (isset($data->override) && 1 === count($data->override)) { 83 | $data->override = reset($data->override); 84 | } 85 | 86 | if (isset($data->order)) { 87 | $data->{'override-order'} = $data->order; 88 | unset($data->order); 89 | } 90 | 91 | if (isset($data->depend)) { 92 | $data->override = $data->depend; 93 | unset($data->depend); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Php/ClassWriter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Php; 13 | 14 | use Puli\Manager\Api\Php\Clazz; 15 | use Puli\Manager\Assert\Assert; 16 | 17 | /** 18 | * Writes {@link Clazz} instances to files. 19 | * 20 | * @since 1.0 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | class ClassWriter 25 | { 26 | /** 27 | * Writes a {@link Clazz} instance to a file. 28 | * 29 | * The directory of the class must have been set. 30 | * 31 | * @param Clazz $class The class to write. 32 | */ 33 | public function writeClass(Clazz $class) 34 | { 35 | Assert::notEmpty($class->getDirectory(), 'The directory of the written class must not be empty.'); 36 | 37 | ob_start(); 38 | 39 | require __DIR__.'/../../res/template/Class.tpl.php'; 40 | 41 | $source = "getDirectory())) { 44 | mkdir($class->getDirectory(), 0777, true); 45 | } 46 | 47 | file_put_contents($class->getFilePath(), $source); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Repository/Mapping/AddPathMappingToModuleFile.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Repository\Mapping; 13 | 14 | use Puli\Manager\Api\Module\RootModuleFile; 15 | use Puli\Manager\Api\Repository\PathMapping; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Adds a path mapping to the root module file. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class AddPathMappingToModuleFile implements AtomicOperation 26 | { 27 | /** 28 | * @var PathMapping 29 | */ 30 | private $mapping; 31 | 32 | /** 33 | * @var RootModuleFile 34 | */ 35 | private $rootModuleFile; 36 | 37 | /** 38 | * @var PathMapping 39 | */ 40 | private $previousMapping; 41 | 42 | public function __construct(PathMapping $mapping, RootModuleFile $rootModuleFile) 43 | { 44 | $this->mapping = $mapping; 45 | $this->rootModuleFile = $rootModuleFile; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function execute() 52 | { 53 | $repositoryPath = $this->mapping->getRepositoryPath(); 54 | 55 | if ($this->rootModuleFile->hasPathMapping($repositoryPath)) { 56 | $this->previousMapping = $this->rootModuleFile->getPathMapping($repositoryPath); 57 | } 58 | 59 | $this->rootModuleFile->addPathMapping($this->mapping); 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function rollback() 66 | { 67 | if ($this->previousMapping) { 68 | $this->rootModuleFile->addPathMapping($this->previousMapping); 69 | } else { 70 | $this->rootModuleFile->removePathMapping($this->mapping->getRepositoryPath()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Repository/Mapping/LoadPathMapping.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Repository\Mapping; 13 | 14 | use Puli\Manager\Api\Module\Module; 15 | use Puli\Manager\Api\Module\ModuleList; 16 | use Puli\Manager\Api\Repository\PathMapping; 17 | use Puli\Manager\Conflict\ModuleConflictDetector; 18 | use Puli\Manager\Transaction\AtomicOperation; 19 | 20 | /** 21 | * Loads a path mapping. 22 | * 23 | * @since 1.0 24 | * 25 | * @author Bernhard Schussek 26 | */ 27 | class LoadPathMapping implements AtomicOperation 28 | { 29 | /** 30 | * @var PathMapping 31 | */ 32 | private $mapping; 33 | 34 | /** 35 | * @var Module 36 | */ 37 | private $containingModule; 38 | 39 | /** 40 | * @var ModuleList 41 | */ 42 | private $modules; 43 | 44 | /** 45 | * @var PathMappingCollection 46 | */ 47 | private $mappings; 48 | 49 | /** 50 | * @var PathMappingCollection 51 | */ 52 | private $mappingsByResource; 53 | 54 | /** 55 | * @var ModuleConflictDetector 56 | */ 57 | private $conflictDetector; 58 | 59 | public function __construct(PathMapping $mapping, Module $containingModule, ModuleList $modules, PathMappingCollection $mappings, PathMappingCollection $mappingsByResource, ModuleConflictDetector $conflictDetector) 60 | { 61 | $this->mapping = $mapping; 62 | $this->containingModule = $containingModule; 63 | $this->modules = $modules; 64 | $this->mappings = $mappings; 65 | $this->mappingsByResource = $mappingsByResource; 66 | $this->conflictDetector = $conflictDetector; 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | */ 72 | public function execute() 73 | { 74 | if ($this->mapping->isLoaded()) { 75 | return; 76 | } 77 | 78 | $this->mapping->load($this->containingModule, $this->modules); 79 | 80 | $moduleName = $this->containingModule->getName(); 81 | 82 | $this->mappings->add($this->mapping); 83 | 84 | foreach ($this->mapping->listRepositoryPaths() as $repositoryPath) { 85 | $this->mappingsByResource->set($repositoryPath, $this->mapping); 86 | $this->conflictDetector->claim($repositoryPath, $moduleName); 87 | } 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | public function rollback() 94 | { 95 | if (!$this->mapping->isLoaded()) { 96 | return; 97 | } 98 | 99 | $moduleName = $this->containingModule->getName(); 100 | 101 | $this->mappings->remove($this->mapping->getRepositoryPath(), $moduleName); 102 | 103 | foreach ($this->mapping->listRepositoryPaths() as $repositoryPath) { 104 | $this->mappingsByResource->remove($repositoryPath, $moduleName); 105 | $this->conflictDetector->release($repositoryPath, $moduleName); 106 | } 107 | 108 | // Unload after iterating, otherwise the paths are gone 109 | $this->mapping->unload(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Repository/Mapping/OverrideConflictingModules.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Repository\Mapping; 13 | 14 | use Puli\Manager\Api\Module\RootModule; 15 | use Puli\Manager\Api\Repository\PathMapping; 16 | use Puli\Manager\Conflict\DependencyGraph; 17 | use Puli\Manager\Transaction\AtomicOperation; 18 | 19 | /** 20 | * Adds an override statement for each module conflicting with the root module. 21 | * 22 | * @since 1.0 23 | * 24 | * @author Bernhard Schussek 25 | */ 26 | class OverrideConflictingModules implements AtomicOperation 27 | { 28 | /** 29 | * @var PathMapping 30 | */ 31 | private $mapping; 32 | 33 | /** 34 | * @var RootModule 35 | */ 36 | private $rootModule; 37 | 38 | /** 39 | * @var DependencyGraph 40 | */ 41 | private $overrideGraph; 42 | 43 | /** 44 | * @var string[] 45 | */ 46 | private $overriddenModules = array(); 47 | 48 | /** 49 | * @var string[] 50 | */ 51 | private $addedEdgesFrom = array(); 52 | 53 | public function __construct(PathMapping $mapping, RootModule $rootModule, DependencyGraph $overrideGraph) 54 | { 55 | $this->mapping = $mapping; 56 | $this->rootModule = $rootModule; 57 | $this->overrideGraph = $overrideGraph; 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public function execute() 64 | { 65 | $rootModuleName = $this->rootModule->getName(); 66 | $rootModuleFile = $this->rootModule->getModuleFile(); 67 | 68 | foreach ($this->mapping->getConflictingModules() as $conflictingModule) { 69 | $moduleName = $conflictingModule->getName(); 70 | 71 | if (!$rootModuleFile->hasDependency($moduleName)) { 72 | $rootModuleFile->addDependency($moduleName); 73 | $this->overriddenModules[] = $moduleName; 74 | } 75 | 76 | if (!$this->overrideGraph->hasDependency($rootModuleName, 77 | $moduleName)) { 78 | $this->overrideGraph->addDependency($rootModuleName, 79 | $moduleName); 80 | $this->addedEdgesFrom[] = $moduleName; 81 | } 82 | } 83 | } 84 | 85 | /** 86 | * {@inheritdoc} 87 | */ 88 | public function rollback() 89 | { 90 | $rootModuleName = $this->rootModule->getName(); 91 | $rootModuleFile = $this->rootModule->getModuleFile(); 92 | 93 | foreach ($this->overriddenModules as $moduleName) { 94 | $rootModuleFile->removeDependency($moduleName); 95 | } 96 | 97 | foreach ($this->addedEdgesFrom as $moduleName) { 98 | $this->overrideGraph->removeDependency($rootModuleName, $moduleName); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Repository/Mapping/PopulateRepository.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Repository\Mapping; 13 | 14 | use Exception; 15 | use Puli\Manager\Api\Repository\PathMapping; 16 | use Puli\Manager\Conflict\DependencyGraph; 17 | use Puli\Manager\Transaction\AtomicOperation; 18 | use Puli\Repository\Api\EditableRepository; 19 | use Puli\Repository\Resource\DirectoryResource; 20 | use Puli\Repository\Resource\FileResource; 21 | 22 | /** 23 | * Inserts all path mappings into the repository. 24 | * 25 | * @since 1.0 26 | * 27 | * @author Bernhard Schussek 28 | */ 29 | class PopulateRepository implements AtomicOperation 30 | { 31 | /** 32 | * @var EditableRepository 33 | */ 34 | private $repo; 35 | 36 | /** 37 | * @var PathMappingCollection 38 | */ 39 | private $mappings; 40 | 41 | /** 42 | * @var DependencyGraph 43 | */ 44 | private $overrideGraph; 45 | 46 | /** 47 | * @var bool 48 | */ 49 | private $added = false; 50 | 51 | public function __construct(EditableRepository $repo, PathMappingCollection $mappings, DependencyGraph $overrideGraph) 52 | { 53 | $this->repo = $repo; 54 | $this->mappings = $mappings; 55 | $this->overrideGraph = $overrideGraph; 56 | } 57 | 58 | /** 59 | * {@inheritdoc} 60 | */ 61 | public function execute() 62 | { 63 | // Quit if no mappings exist 64 | if (!$moduleNames = $this->mappings->getModuleNames()) { 65 | return; 66 | } 67 | 68 | $sortedNames = $this->overrideGraph->getSortedModuleNames($moduleNames); 69 | 70 | try { 71 | foreach ($sortedNames as $moduleName) { 72 | foreach ($this->getEnabledMappingsByModuleName($moduleName) as $repositoryPath => $mapping) { 73 | foreach ($mapping->getFilesystemPaths() as $filesystemPath) { 74 | $this->repo->add($repositoryPath, $this->createResource($filesystemPath)); 75 | $this->added = true; 76 | } 77 | } 78 | } 79 | } catch (Exception $e) { 80 | $this->repo->clear(); 81 | 82 | throw $e; 83 | } 84 | } 85 | 86 | /** 87 | * {@inheritdoc} 88 | */ 89 | public function rollback() 90 | { 91 | if ($this->added) { 92 | $this->repo->clear(); 93 | } 94 | } 95 | 96 | /** 97 | * @param string $moduleName 98 | * 99 | * @return PathMapping[] 100 | */ 101 | private function getEnabledMappingsByModuleName($moduleName) 102 | { 103 | $mappingsToAdd = array(); 104 | 105 | foreach ($this->mappings->listByModuleName($moduleName) as $repositoryPath => $mapping) { 106 | if ($mapping->isEnabled()) { 107 | // Remove duplicates 108 | $mappingsToAdd[$mapping->getRepositoryPath()] = $mapping; 109 | } 110 | } 111 | 112 | return $mappingsToAdd; 113 | } 114 | 115 | private function createResource($filesystemPath) 116 | { 117 | return is_dir($filesystemPath) 118 | ? new DirectoryResource($filesystemPath) 119 | : new FileResource($filesystemPath); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Repository/Mapping/RemovePathMappingFromModuleFile.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Repository\Mapping; 13 | 14 | use Puli\Manager\Api\Module\RootModuleFile; 15 | use Puli\Manager\Api\Repository\PathMapping; 16 | use Puli\Manager\Transaction\AtomicOperation; 17 | 18 | /** 19 | * Removes a path mapping from the root module file. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | class RemovePathMappingFromModuleFile implements AtomicOperation 26 | { 27 | /** 28 | * @var string 29 | */ 30 | private $repositoryPath; 31 | 32 | /** 33 | * @var RootModuleFile 34 | */ 35 | private $rootModuleFile; 36 | 37 | /** 38 | * @var PathMapping 39 | */ 40 | private $previousMapping; 41 | 42 | public function __construct($repositoryPath, RootModuleFile $rootModuleFile) 43 | { 44 | $this->repositoryPath = $repositoryPath; 45 | $this->rootModuleFile = $rootModuleFile; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function execute() 52 | { 53 | if ($this->rootModuleFile->hasPathMapping($this->repositoryPath)) { 54 | $this->previousMapping = $this->rootModuleFile->getPathMapping($this->repositoryPath); 55 | $this->rootModuleFile->removePathMapping($this->repositoryPath); 56 | } 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function rollback() 63 | { 64 | if ($this->previousMapping) { 65 | $this->rootModuleFile->addPathMapping($this->previousMapping); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Transaction/AtomicOperation.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Transaction; 13 | 14 | /** 15 | * An atomic operation. 16 | * 17 | * Atomic operations must not cause any side effects if their execution fails. 18 | * Additionally, atomic operations support the method {@link rollback()} which 19 | * undoes any side effects caused by {@link execute()}. 20 | * 21 | * @since 1.0 22 | * 23 | * @author Bernhard Schussek 24 | */ 25 | interface AtomicOperation 26 | { 27 | /** 28 | * Executes the operation. 29 | * 30 | * If the execution fails, this method must not cause any side effects in 31 | * the system. 32 | */ 33 | public function execute(); 34 | 35 | /** 36 | * Undoes the side effects of the operation. 37 | * 38 | * This method is called if the operation needs to be reverted. The method 39 | * is only called if {@link execute()} completed successfully. 40 | */ 41 | public function rollback(); 42 | } 43 | -------------------------------------------------------------------------------- /src/Transaction/InterceptedOperation.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Transaction; 13 | 14 | use Exception; 15 | use Puli\Manager\Assert\Assert; 16 | 17 | /** 18 | * Adds support for interceptors to an atomic operation. 19 | * 20 | * The operation and its interceptors are passed to the constructor. All 21 | * interceptors must implement {@link OperationInterceptor}. 22 | * 23 | * The {@link OperationInterceptor} interface supports two methods: 24 | * 25 | * * {@link OperationInterceptor::postExecute()} is invoked after executing 26 | * the operation. If the execution fails, the interceptor is not invoked. 27 | * If the interceptor fails, the operation is rolled back. 28 | * * {@link OperationInterceptor::postRollback()} is invoked after rolling 29 | * back the operation. Consequently, this method is also called when 30 | * {@link OperationInterceptor::postExecute()} fails. 31 | * 32 | * @since 1.0 33 | * 34 | * @author Bernhard Schussek 35 | */ 36 | class InterceptedOperation implements AtomicOperation 37 | { 38 | /** 39 | * @var AtomicOperation 40 | */ 41 | private $operation; 42 | 43 | /** 44 | * @var OperationInterceptor[] 45 | */ 46 | private $interceptors; 47 | 48 | /** 49 | * @var OperationInterceptor[] 50 | */ 51 | private $interceptorsForRollback; 52 | 53 | /** 54 | * Adds support for interceptors to an atomic operation. 55 | * 56 | * @param AtomicOperation $operation The operation. 57 | * @param OperationInterceptor|OperationInterceptor[] $interceptors The interceptor(s). 58 | */ 59 | public function __construct(AtomicOperation $operation, $interceptors) 60 | { 61 | $interceptors = is_array($interceptors) ? $interceptors : array($interceptors); 62 | 63 | Assert::allIsInstanceOf($interceptors, __NAMESPACE__.'\OperationInterceptor'); 64 | 65 | $this->operation = $operation; 66 | $this->interceptors = $interceptors; 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | */ 72 | public function execute() 73 | { 74 | // no rollback if execute() fails 75 | $this->operation->execute(); 76 | 77 | try { 78 | // rollback if postExecute() fails, since execute() is already 79 | // completed 80 | foreach ($this->interceptors as $interceptor) { 81 | $this->interceptorsForRollback[] = $interceptor; 82 | $interceptor->postExecute(); 83 | } 84 | } catch (Exception $e) { 85 | $this->rollback(); 86 | } 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | */ 92 | public function rollback() 93 | { 94 | $this->operation->rollback(); 95 | 96 | // Only launch interceptors whose postExecute() method was called 97 | foreach ($this->interceptorsForRollback as $interceptor) { 98 | $interceptor->postRollback(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Transaction/OperationInterceptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Transaction; 13 | 14 | /** 15 | * An interceptor for an atomic operation. 16 | * 17 | * See {@link InterceptedOperation} for a description of interceptors. 18 | * 19 | * @since 1.0 20 | * 21 | * @author Bernhard Schussek 22 | * 23 | * @see InterceptedOperation 24 | */ 25 | interface OperationInterceptor 26 | { 27 | /** 28 | * Invoked after executing the operation. 29 | * 30 | * If this method fails, the operation is rolled back. 31 | * {@link postRollback()} will be called after the rollback and can be used 32 | * for cleaning up side effects. 33 | */ 34 | public function postExecute(); 35 | 36 | /** 37 | * Invoked after rolling back the operation. 38 | * 39 | * Use this method to revert the side effects of {@link postExecute()} in 40 | * case the operation needs to be rolled back. 41 | */ 42 | public function postRollback(); 43 | } 44 | -------------------------------------------------------------------------------- /src/Transaction/Transaction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Puli\Manager\Transaction; 13 | 14 | /** 15 | * A sequence of atomic operations that can be rolled back. 16 | * 17 | * A transaction is a sequence of atomic operations. The term "atomic" means 18 | * that the operation either executes successfully or fails without side 19 | * effects. All atomic operations must implement {@link AtomicOperation}. 20 | * 21 | * If an error occurs during the execution of a transaction, all completed 22 | * operations can be undone by calling {@link rollback()}. If no error 23 | * occurred, the transaction can be finalized by calling {@link commit()}. 24 | * 25 | * @since 1.0 26 | * 27 | * @author Bernhard Schussek 28 | */ 29 | class Transaction 30 | { 31 | /** 32 | * @var AtomicOperation[] 33 | */ 34 | private $completedOperations = array(); 35 | 36 | /** 37 | * Executes an operation in the scope of this transaction. 38 | * 39 | * @param AtomicOperation $operation The operation to execute. 40 | */ 41 | public function execute(AtomicOperation $operation) 42 | { 43 | $operation->execute(); 44 | 45 | $this->completedOperations[] = $operation; 46 | } 47 | 48 | /** 49 | * Rolls back all executed operations. 50 | * 51 | * Only operations that executed successfully are rolled back. 52 | */ 53 | public function rollback() 54 | { 55 | for ($i = count($this->completedOperations) - 1; $i >= 0; --$i) { 56 | $this->completedOperations[$i]->rollback(); 57 | } 58 | 59 | $this->completedOperations = array(); 60 | } 61 | 62 | /** 63 | * Commits the transaction. 64 | */ 65 | public function commit() 66 | { 67 | $this->completedOperations = array(); 68 | } 69 | } 70 | --------------------------------------------------------------------------------