{"action":"created","issue":{"url":"https://api.github.com/repos/puli/issues/issues/148","labels_url":"https://api.github.com/repos/puli/issues/issues/148/labels{/name}","comments_url":"https://api.github.com/repos/puli/issues/issues/148/comments","events_url":"https://api.github.com/repos/puli/issues/issues/148/events","html_url":"https://github.com/puli/issues/issues/148","id":122072074,"number":148,"title":"Cache combined puli.json files of all installed packages","user":{"login":"webmozart","id":176399,"avatar_url":"https://avatars.githubusercontent.com/u/176399?v=3","gravatar_id":"","url":"https://api.github.com/users/webmozart","html_url":"https://github.com/webmozart","followers_url":"https://api.github.com/users/webmozart/followers","following_url":"https://api.github.com/users/webmozart/following{/other_user}","gists_url":"https://api.github.com/users/webmozart/gists{/gist_id}","starred_url":"https://api.github.com/users/webmozart/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/webmozart/subscriptions","organizations_url":"https://api.github.com/users/webmozart/orgs","repos_url":"https://api.github.com/users/webmozart/repos","events_url":"https://api.github.com/users/webmozart/events{/privacy}","received_events_url":"https://api.github.com/users/webmozart/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/puli/issues/labels/Feature","name":"Feature","color":"009800"},{"url":"https://api.github.com/repos/puli/issues/labels/Manager","name":"Manager","color":"e6e6e6"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/puli/issues/milestones/5","html_url":"https://github.com/puli/issues/milestones/1.0-beta10","labels_url":"https://api.github.com/repos/puli/issues/milestones/5/labels","id":1331804,"number":5,"title":"1.0-beta10","description":"","creator":{"login":"webmozart","id":176399,"avatar_url":"https://avatars.githubusercontent.com/u/176399?v=3","gravatar_id":"","url":"https://api.github.com/users/webmozart","html_url":"https://github.com/webmozart","followers_url":"https://api.github.com/users/webmozart/followers","following_url":"https://api.github.com/users/webmozart/following{/other_user}","gists_url":"https://api.github.com/users/webmozart/gists{/gist_id}","starred_url":"https://api.github.com/users/webmozart/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/webmozart/subscriptions","organizations_url":"https://api.github.com/users/webmozart/orgs","repos_url":"https://api.github.com/users/webmozart/repos","events_url":"https://api.github.com/users/webmozart/events{/privacy}","received_events_url":"https://api.github.com/users/webmozart/received_events","type":"User","site_admin":false},"open_issues":8,"closed_issues":16,"state":"open","created_at":"2015-09-30T18:09:01Z","updated_at":"2015-12-14T16:00:41Z","due_on":"2015-10-30T23:00:00Z","closed_at":null},"comments":0,"created_at":"2015-12-14T16:00:41Z","updated_at":"2015-12-14T16:08:01Z","closed_at":null,"body":"Currently, whenever information about installed packages needs to be accessed, the complete PackageManager must be instantiated and loaded with the puli.json files of all installed packages. This process is a bit expensive, first of all due to file system accesses, and second due to validation and migration of the contents of each file. Vendor dependencies only (potentially) change when running \"composer update\", so doing that work every time that the manager boots is quite useless.\r\n\r\nI suggest that we cache the contents of all puli.json file in a puli.json.cache file. For that, we need the following classes:\r\n\r\n* CacheFile: represents the puli.json.cache file\r\n* CacheManager: manages loading and generating CacheFiles \r\n* Config::CACHE_FILE: new configuration entry that stores the path of the cache file\r\n* DefaultConfig: CACHE_FILE is '{$puli-dir}/puli.json.cache' by default\r\n\r\nHere's more information on the methods in the new classes/interfaces:\r\n\r\n```php\r\nnamespace Puli\\Manager\\Api\\Cache;\r\n\r\ninterface CacheManager\r\n{\r\n    public function getContext();\r\n\r\n    public function getCacheFile();\r\n\r\n    public function refreshCacheFile();\r\n\r\n    public function getPackage($name);\r\n\r\n    public function getRootPackage();\r\n\r\n    public function getPackages();\r\n\r\n    public function findPackages(Expression $expr);\r\n\r\n    public function hasPackage($name);\r\n\r\n    public function hasPackages(Expression $expr = null);\r\n}\r\n```\r\n\r\n* The `*Package*()` methods are similar to those in `PackageManager`\r\n* `refreshCacheFile()` generates a new cache file if (i) none exists or (ii) the change time of any of the puli.json files is newer than that of the cache file\r\n* `getCacheFile()` reads and returns the cache file. If none exists -> `refreshCacheFile()`.\r\n\r\nThe manager interface is implemented by `Puli\\Manager\\Cache\\CacheManagerImpl`. New instances are returned by `Puli::getCacheManager()`.\r\n\r\n```php\r\nnamespace Puli\\Manager\\Api\\Cache;\r\n\r\nclass CacheFile\r\n{\r\n    /**\r\n     * @var string|null\r\n     */\r\n    private $path;\r\n\r\n    /**\r\n     * @var PackageFile[]\r\n     */\r\n    private $packageFiles = array();\r\n\r\n    public function __construct($path = null)\r\n\r\n    public function getPath()\r\n\r\n    public function setPackageFiles(array $packageFiles)\r\n\r\n    public function addPackageFile(PackageFile $packageFile)\r\n\r\n    public function removePackageFile($packageName)\r\n\r\n    public function clearPackageFiles()\r\n\r\n    public function getPackageFile($packageName)\r\n\r\n    public function getPackageFiles()\r\n\r\n    public function hasPackageFile($packageName)\r\n\r\n    public function hasPackageFiles()\r\n}\r\n```\r\n\r\n* The cache file is similar to other such classes in the manager, like ConfigFile, RootPackageFile or PackageFile.\r\n\r\nUnlike PackageFile, which has a version number and can be migrated between different versions, CacheFile always contains the PackageFile JSON in the latest version."},"comment":{"url":"https://api.github.com/repos/puli/issues/issues/comments/164477766","html_url":"https://github.com/puli/issues/issues/148#issuecomment-164477766","issue_url":"https://api.github.com/repos/puli/issues/issues/148","id":164477766,"user":{"login":"webmozart","id":176399,"avatar_url":"https://avatars.githubusercontent.com/u/176399?v=3","gravatar_id":"","url":"https://api.github.com/users/webmozart","html_url":"https://github.com/webmozart","followers_url":"https://api.github.com/users/webmozart/followers","following_url":"https://api.github.com/users/webmozart/following{/other_user}","gists_url":"https://api.github.com/users/webmozart/gists{/gist_id}","starred_url":"https://api.github.com/users/webmozart/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/webmozart/subscriptions","organizations_url":"https://api.github.com/users/webmozart/orgs","repos_url":"https://api.github.com/users/webmozart/repos","events_url":"https://api.github.com/users/webmozart/events{/privacy}","received_events_url":"https://api.github.com/users/webmozart/received_events","type":"User","site_admin":false},"created_at":"2015-12-14T16:08:01Z","updated_at":"2015-12-14T16:08:01Z","body":"@msojda You can work on this, if you like. :)"}}