{"action":"created","comment":{"url":"https://api.github.com/repos/piwik/piwik/pulls/comments/28120971","id":28120971,"diff_hunk":"@@ -0,0 +1,176 @@\n+<?php\n+/**\n+ * Piwik - free/libre analytics platform\n+ *\n+ * @link http://piwik.org\n+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later\n+ */\n+\n+namespace Piwik\\Application;\n+\n+use DI\\Container;\n+use Piwik\\Application\\Kernel\\EnvironmentValidator;\n+use Piwik\\Application\\Kernel\\GlobalSettingsProvider;\n+use Piwik\\Application\\Kernel\\PluginList;\n+use Piwik\\Application\\Kernel\\PluginList\\IniPluginList;\n+use Piwik\\Application\\Kernel\\GlobalSettingsProvider\\IniSettingsProvider;\n+use Piwik\\Container\\ContainerFactory;\n+use Piwik\\Container\\StaticContainer;\n+use Piwik\\Piwik;\n+\n+/**\n+ * Encapsulates Piwik environment setup and access.\n+ *\n+ * The Piwik environment consists of two main parts: the kernel and the DI container.\n+ *\n+ * The 'kernel' is the core part of Piwik that cannot be modified / extended through the DI container.\n+ * It includes components that are required to create the DI container.\n+ *\n+ * Currently the only objects in the 'kernel' are a GlobalSettingsProvider object and a\n+ * PluginList object. The GlobalSettingsProvider object is required for the current PluginList\n+ * implementation and for checking whether Development mode is enabled. The PluginList is\n+ * needed in order to determine what plugins are activated, since plugins can provide their\n+ * own DI configuration.\n+ *\n+ * The DI container contains every other Piwik object, including the Plugin\\Manager,\n+ * plugin API instances, dependent services, etc. Plugins and users can override/extend\n+ * the objects in this container.\n+ *\n+ * NOTE: DI support in Piwik is currently a work in process; not everything is currently\n+ * stored in the DI container, but we are working towards this.\n+ */\n+class Environment\n+{\n+    /**\n+     * @var string\n+     */\n+    private $environment;\n+\n+    /**\n+     * @var array\n+     */\n+    private $definitions;\n+\n+    /**\n+     * @var Container\n+     */\n+    private $container;\n+\n+    /**\n+     * @var GlobalSettingsProvider\n+     */\n+    private $globalSettingsProvider;\n+\n+    /**\n+     * @var PluginList\n+     */\n+    private $pluginList;\n+\n+    /**\n+     * @param string $environment\n+     * @param array $definitions\n+     */\n+    public function __construct($environment, array $definitions = array())\n+    {\n+        $this->environment = $environment;\n+        $this->definitions = $definitions;\n+    }\n+\n+    /**\n+     * Initializes the kernel globals and DI container.\n+     */\n+    public function init()\n+    {\n+        $this->container = $this->createContainer();\n+\n+        StaticContainer::set($this->container);\n+\n+        $this->validateEnvironment();\n+\n+        Piwik::postEvent('Environment.bootstrapped'); // this event should be removed eventually\n+    }\n+\n+    /**\n+     * Returns the DI container. All Piwik objects for a specific Piwik instance should be stored\n+     * in this container.\n+     *\n+     * @return Container\n+     */\n+    public function getContainer()\n+    {\n+        return $this->container;\n+    }\n+\n+    /**\n+     * Returns the Piwik object for this Environment's Piwik instance by ID.\n+     *\n+     * @param string $id\n+     * @return mixed\n+     * @throws \\DI\\NotFoundException\n+     */\n+    public function get($id)\n+    {\n+        return $this->container->get($id);\n+    }\n+\n+    /**\n+     * @link http://php-di.org/doc/container-configuration.html\n+     */\n+    private function createContainer()\n+    {\n+        $pluginList = $this->getPluginListCached();\n+        $settings = $this->getGlobalSettingsCached();\n+        $definitions = array_merge(StaticContainer::getDefinitions(), $this->definitions);\n+\n+        $containerFactory = new ContainerFactory($pluginList, $settings, $this->environment, $definitions);\n+        return $containerFactory->create();\n+    }\n+\n+    protected function getGlobalSettingsCached()\n+    {\n+        if ($this->globalSettingsProvider === null) {\n+            $this->globalSettingsProvider = $this->getGlobalSettings();\n+        }\n+        return $this->globalSettingsProvider;\n+    }\n+\n+    protected function getPluginListCached()\n+    {\n+        if ($this->pluginList === null) {\n+            $this->pluginList = $this->getPluginList();\n+        }\n+        return $this->pluginList;\n+    }\n+\n+    /**\n+     * Returns the kernel global GlobalSettingsProvider object. Derived classes can override this method\n+     * to provide a different implementation.\n+     *\n+     * @return null|GlobalSettingsProvider\n+     */\n+    protected function getGlobalSettings()\n+    {\n+        // TODO: need to be able to set path global/local/etc. which is in DI... for now works because TestingEnvironment creates\n+        //       singleton instance before this method.\n+        return IniSettingsProvider::getSingletonInstance();\n+    }\n+\n+    /**\n+     * Returns the kernel global PluginList object. Derived classes can override this method to\n+     * provide a different implementation.\n+     *\n+     * @return PluginList\n+     */\n+    protected function getPluginList()\n+    {\n+        // TODO: in tracker should only load tracker plugins. can't do properly until tracker entrypoint is encapsulated.\n+        return new IniPluginList($this->getGlobalSettingsCached());","path":"core/Application/Environment.php","position":167,"original_position":167,"commit_id":"5d57e10591a9e7e394f649cc67ea93ed14c47292","original_commit_id":"5d57e10591a9e7e394f649cc67ea93ed14c47292","user":{"login":"mnapoli","id":720328,"avatar_url":"https://avatars.githubusercontent.com/u/720328?v=3","gravatar_id":"","url":"https://api.github.com/users/mnapoli","html_url":"https://github.com/mnapoli","followers_url":"https://api.github.com/users/mnapoli/followers","following_url":"https://api.github.com/users/mnapoli/following{/other_user}","gists_url":"https://api.github.com/users/mnapoli/gists{/gist_id}","starred_url":"https://api.github.com/users/mnapoli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mnapoli/subscriptions","organizations_url":"https://api.github.com/users/mnapoli/orgs","repos_url":"https://api.github.com/users/mnapoli/repos","events_url":"https://api.github.com/users/mnapoli/events{/privacy}","received_events_url":"https://api.github.com/users/mnapoli/received_events","type":"User","site_admin":false},"body":"Does that mean that the tracker will now load all the plugins?","created_at":"2015-04-10T03:36:21Z","updated_at":"2015-04-10T03:36:21Z","html_url":"https://github.com/piwik/piwik/pull/7644#discussion_r28120971","pull_request_url":"https://api.github.com/repos/piwik/piwik/pulls/7644","_links":{"self":{"href":"https://api.github.com/repos/piwik/piwik/pulls/comments/28120971"},"html":{"href":"https://github.com/piwik/piwik/pull/7644#discussion_r28120971"},"pull_request":{"href":"https://api.github.com/repos/piwik/piwik/pulls/7644"}}},"pull_request":{"url":"https://api.github.com/repos/piwik/piwik/pulls/7644","id":32825009,"html_url":"https://github.com/piwik/piwik/pull/7644","diff_url":"https://github.com/piwik/piwik/pull/7644.diff","patch_url":"https://github.com/piwik/piwik/pull/7644.patch","issue_url":"https://api.github.com/repos/piwik/piwik/issues/7644","number":7644,"state":"open","locked":false,"title":"Moby DIoC: Step 1 - Piwik environment encapsulation","user":{"login":"diosmosis","id":125140,"avatar_url":"https://avatars.githubusercontent.com/u/125140?v=3","gravatar_id":"","url":"https://api.github.com/users/diosmosis","html_url":"https://github.com/diosmosis","followers_url":"https://api.github.com/users/diosmosis/followers","following_url":"https://api.github.com/users/diosmosis/following{/other_user}","gists_url":"https://api.github.com/users/diosmosis/gists{/gist_id}","starred_url":"https://api.github.com/users/diosmosis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/diosmosis/subscriptions","organizations_url":"https://api.github.com/users/diosmosis/orgs","repos_url":"https://api.github.com/users/diosmosis/repos","events_url":"https://api.github.com/users/diosmosis/events{/privacy}","received_events_url":"https://api.github.com/users/diosmosis/received_events","type":"User","site_admin":false},"body":"Call me Ishmael.\r\n\r\nThis pull requests provides logic to encapsulate the entire environment setup logic in a single class. As a proof of value, the Plugin\\Manager has been successfully added to the DI container.\r\n\r\n## New Concepts\r\n\r\nThis PR introduces one new concept:\r\n\r\n**Kernel Objects**\r\n\r\nCurrently in Piwik, most services are singletons, despite the presence of a DI container. Ideally, all of these singletons should be moved to the DI container, however, this is non-trivial since in order to create the container, some singletons are used (specifically Config & Plugin\\Manager).\r\n\r\nIn order to decouple container creation from these singletons, I introduce the concept of Piwik's **kernel**, which is the core part of Piwik that cannot be overridden through DI (and thus by plugins). Currently, only two types of objects exist here: the PluginList & the GlobalSettingsProvider.\r\n\r\nThe PluginList currently returns the list of activated plugins, so the container can load plugin-specific config. The GlobalSettingsProvider provides config information used to create the container (and for the default implementation of PluginList).\r\n\r\nThese objects are created before the DI container is created, but they are placed in the container so other objects can access them w/o having to treat them as singletons.\r\n\r\nThough these objects cannot be overridden through DI, it is my intent to allow this eventually, through Application encapsulation (see the next steps list).\r\n\r\n## List of changes\r\n\r\n1. Introduction of Environment class\r\n\r\n    This class encapsulates the creation of kernel objects & then the DI container. It also encapsulates environment validation, which before, was in several places, including Config.php/FrontController.php/CronArchive.php\r\n\r\n2. Introduction of PluginList kernel object interface + IniPluginList implementation\r\n\r\n    This class is used to create the container, and so cannot be created through DI.\r\n\r\n    **Rationale:** I created a new interface for this functionality, as it could be useful later. When we modify plugins to be available through composer, we could create a new implementation to manage the activated plugin list using composer.json or another file.\r\n\r\n3. Introduction of GlobalSettingsProvider kernel object interface + IniSettingsProvider implementation\r\n\r\n    This class is used to create the container, and so cannot be created through DI.\r\n\r\n    **Rationale:** I created a new interface, because it might be useful to provide new implementations later. Eg, during the meetup, pro devs described needing to store most config values in the DB so propagating changes through a load balanced environment would be easier. Creating another implementation of this class (when it's possible to) could provide a possible solution.\r\n\r\n4. Moving all environment validation logic to EnvironmentValidator class (another kernel object).\r\n\r\n    Currently in Piwik, Environment validation is done manually in Config, FrontController & CronArchive. This PR moves all that validation to a new class used by Environment. The EnvironmentValidator class will check if the global.ini.php/config.ini.php file is available, and based on the current execution environment (ie, tracker/cli/web), will display an error or start the installation process.\r\n\r\n    There is also a system test that verifies this behaviour.\r\n\r\n5. Removed ability to change config files used by a Config instance\r\n\r\n    In order to fix tests after this change and to allow INI files to be read before Config is created, I had to ensure that files used by Config couldn't change at any random point during test execution. Instead, one must create a new singleton instance. This is closer to overriding via DI, so should help in putting Config in DI.\r\n\r\n    This change includes:\r\n\r\n    * Deprecating init(), setTestEnvironment(), clear() and removing uses in core Piwik code.\r\n    * Making reload() protected.\r\n\r\n6. Moved Config::encodeValues/Config::decodeValues to IniFileChain.\r\n\r\n    Since the encoding/decoding logic is used to avoid some INI parsing bugs, it should be part of IniFileChain, not just the Piwik-specific Config class.\r\n\r\n7. Lots and lots of hacks to test code\r\n\r\n    These changes are changes to TestingEnvironment.php, the introduction of TestConfig.php and changes to test case classes. These are all there just to make tests pass, and should be considered temporary. Eventually, I will clean up test code significantly.\r\n\r\n8. Tweaks to CronArchive.php environment setup\r\n\r\n    CronArchive.php has some unnecessary code for environment setup. Now, since archiving is initiated through archive.php & through core:archive, the environment will already be setup, so there's no need to set the host and create the Config instance. I verified it works w/ --piwik-domain & w/ `Host: ...` HTTP header.\r\n\r\n9. Change to core\\IP.php\r\n\r\n    There's a small change here. For some reason, the error silencing on the statement `@Config::getInstance()->General['...'];` didn't work unless it was split into two statements.\r\n\r\n10. Plugin\\Manager added to DI\r\n\r\n     Not much to say about this. `::getInstance()` is still there, as more objects are added to DI, we can perhaps remove it.\r\n\r\n11. Removed cli-script-bootstrap.php\r\n\r\n    No longer used, so removed.\r\n\r\n## Next Steps\r\n\r\n1. Application Encapsulation.\r\n\r\n    Involves encapsulating every entry point in a class that creates an Environment. For example, the tracker can be encapsulated in a TrackerApplication class that provides a `public function track($params)` method. The piwik.php file should then be just:\r\n\r\n    ```\r\n    require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Application.php'; // handles autoload requiring + whatever else\r\n\r\n    $tracker = new TrackerApplication();\r\n    $tracker->track($_GET + $_POST, $_SERVER, ...);\r\n    ```\r\n\r\n2. Moving Config class to DI & deprecating, merging with IniSettingsProvider\r\n\r\n    If we merge the rest of Config logic w/ IniSettingsProvider, we will have one less singleton. Obviously, we can't get rid of Config because of it's pervasive use, but it would become just a view of IniSettingsProvider.\r\n\r\n    Removing it completely will force cleaning up the test environment setup code (which is do-able, but I cannot think of a concrete solution w/o starting to work on it).\r\n\r\n3. Filling out PluginList description\r\n\r\n   We can move other plugin list manipulation logic from Plugin\\Manager to PluginList + IniPluginList to make it easier in the future to install plugins through Composer. We can potentially create an implementation that uses composer.json or another file.\r\n\r\n## Post-merge TODO\r\n\r\n* [ ] Get builds using CloudFixture to pass. I have a branch that does some of the work, but I haven't updated in a while. Waiting for this to be merged.\r\n* [ ] Add system test to make sure cron archiving (via cli + web) works when another piwik domain is used. Requires --dry-run parameter to CronArchive so tests don't become too slow.","created_at":"2015-04-08T03:05:05Z","updated_at":"2015-04-10T03:36:21Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2c8a9afd5c8628801e7a9e6291c6b7a21cd7c65f","assignee":null,"milestone":{"url":"https://api.github.com/repos/piwik/piwik/milestones/76","html_url":"https://github.com/piwik/piwik/milestones/Piwik%202.13.0","labels_url":"https://api.github.com/repos/piwik/piwik/milestones/76/labels","id":969583,"number":76,"title":"Piwik 2.13.0","description":"Pool of tickets we pick issues to work on. This release may include DB schema upgrades.\r\n","creator":{"login":"mattab","id":466765,"avatar_url":"https://avatars.githubusercontent.com/u/466765?v=3","gravatar_id":"","url":"https://api.github.com/users/mattab","html_url":"https://github.com/mattab","followers_url":"https://api.github.com/users/mattab/followers","following_url":"https://api.github.com/users/mattab/following{/other_user}","gists_url":"https://api.github.com/users/mattab/gists{/gist_id}","starred_url":"https://api.github.com/users/mattab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattab/subscriptions","organizations_url":"https://api.github.com/users/mattab/orgs","repos_url":"https://api.github.com/users/mattab/repos","events_url":"https://api.github.com/users/mattab/events{/privacy}","received_events_url":"https://api.github.com/users/mattab/received_events","type":"User","site_admin":false},"open_issues":42,"closed_issues":60,"state":"open","created_at":"2015-02-07T01:34:22Z","updated_at":"2015-04-10T03:06:55Z","due_on":"2015-04-21T12:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/piwik/piwik/pulls/7644/commits","review_comments_url":"https://api.github.com/repos/piwik/piwik/pulls/7644/comments","review_comment_url":"https://api.github.com/repos/piwik/piwik/pulls/comments{/number}","comments_url":"https://api.github.com/repos/piwik/piwik/issues/7644/comments","statuses_url":"https://api.github.com/repos/piwik/piwik/statuses/5d57e10591a9e7e394f649cc67ea93ed14c47292","head":{"label":"piwik:config_step_2","ref":"config_step_2","sha":"5d57e10591a9e7e394f649cc67ea93ed14c47292","user":{"login":"piwik","id":698038,"avatar_url":"https://avatars.githubusercontent.com/u/698038?v=3","gravatar_id":"","url":"https://api.github.com/users/piwik","html_url":"https://github.com/piwik","followers_url":"https://api.github.com/users/piwik/followers","following_url":"https://api.github.com/users/piwik/following{/other_user}","gists_url":"https://api.github.com/users/piwik/gists{/gist_id}","starred_url":"https://api.github.com/users/piwik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piwik/subscriptions","organizations_url":"https://api.github.com/users/piwik/orgs","repos_url":"https://api.github.com/users/piwik/repos","events_url":"https://api.github.com/users/piwik/events{/privacy}","received_events_url":"https://api.github.com/users/piwik/received_events","type":"Organization","site_admin":false},"repo":{"id":1548202,"name":"piwik","full_name":"piwik/piwik","owner":{"login":"piwik","id":698038,"avatar_url":"https://avatars.githubusercontent.com/u/698038?v=3","gravatar_id":"","url":"https://api.github.com/users/piwik","html_url":"https://github.com/piwik","followers_url":"https://api.github.com/users/piwik/followers","following_url":"https://api.github.com/users/piwik/following{/other_user}","gists_url":"https://api.github.com/users/piwik/gists{/gist_id}","starred_url":"https://api.github.com/users/piwik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piwik/subscriptions","organizations_url":"https://api.github.com/users/piwik/orgs","repos_url":"https://api.github.com/users/piwik/repos","events_url":"https://api.github.com/users/piwik/events{/privacy}","received_events_url":"https://api.github.com/users/piwik/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/piwik/piwik","description":"Liberating Web Analytics. Star us on Github? +1. Piwik is the leading open alternative to Google Analytics that gives you full control over your data. Piwik lets you easily collect data from websites, apps & IoT and visualise this data and extract insights. Privacy is built-in. We love Pull Requests! ","fork":false,"url":"https://api.github.com/repos/piwik/piwik","forks_url":"https://api.github.com/repos/piwik/piwik/forks","keys_url":"https://api.github.com/repos/piwik/piwik/keys{/key_id}","collaborators_url":"https://api.github.com/repos/piwik/piwik/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/piwik/piwik/teams","hooks_url":"https://api.github.com/repos/piwik/piwik/hooks","issue_events_url":"https://api.github.com/repos/piwik/piwik/issues/events{/number}","events_url":"https://api.github.com/repos/piwik/piwik/events","assignees_url":"https://api.github.com/repos/piwik/piwik/assignees{/user}","branches_url":"https://api.github.com/repos/piwik/piwik/branches{/branch}","tags_url":"https://api.github.com/repos/piwik/piwik/tags","blobs_url":"https://api.github.com/repos/piwik/piwik/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/piwik/piwik/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/piwik/piwik/git/refs{/sha}","trees_url":"https://api.github.com/repos/piwik/piwik/git/trees{/sha}","statuses_url":"https://api.github.com/repos/piwik/piwik/statuses/{sha}","languages_url":"https://api.github.com/repos/piwik/piwik/languages","stargazers_url":"https://api.github.com/repos/piwik/piwik/stargazers","contributors_url":"https://api.github.com/repos/piwik/piwik/contributors","subscribers_url":"https://api.github.com/repos/piwik/piwik/subscribers","subscription_url":"https://api.github.com/repos/piwik/piwik/subscription","commits_url":"https://api.github.com/repos/piwik/piwik/commits{/sha}","git_commits_url":"https://api.github.com/repos/piwik/piwik/git/commits{/sha}","comments_url":"https://api.github.com/repos/piwik/piwik/comments{/number}","issue_comment_url":"https://api.github.com/repos/piwik/piwik/issues/comments{/number}","contents_url":"https://api.github.com/repos/piwik/piwik/contents/{+path}","compare_url":"https://api.github.com/repos/piwik/piwik/compare/{base}...{head}","merges_url":"https://api.github.com/repos/piwik/piwik/merges","archive_url":"https://api.github.com/repos/piwik/piwik/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/piwik/piwik/downloads","issues_url":"https://api.github.com/repos/piwik/piwik/issues{/number}","pulls_url":"https://api.github.com/repos/piwik/piwik/pulls{/number}","milestones_url":"https://api.github.com/repos/piwik/piwik/milestones{/number}","notifications_url":"https://api.github.com/repos/piwik/piwik/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/piwik/piwik/labels{/name}","releases_url":"https://api.github.com/repos/piwik/piwik/releases{/id}","created_at":"2011-03-30T21:18:17Z","updated_at":"2015-04-10T02:48:29Z","pushed_at":"2015-04-10T02:59:00Z","git_url":"git://github.com/piwik/piwik.git","ssh_url":"git@github.com:piwik/piwik.git","clone_url":"https://github.com/piwik/piwik.git","svn_url":"https://github.com/piwik/piwik","homepage":"http://piwik.org/","size":284907,"stargazers_count":3688,"watchers_count":3688,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":675,"mirror_url":null,"open_issues_count":947,"forks":675,"open_issues":947,"watchers":3688,"default_branch":"master"}},"base":{"label":"piwik:master","ref":"master","sha":"113b4f638d6448b3682f2e7ca68d3198e76c0694","user":{"login":"piwik","id":698038,"avatar_url":"https://avatars.githubusercontent.com/u/698038?v=3","gravatar_id":"","url":"https://api.github.com/users/piwik","html_url":"https://github.com/piwik","followers_url":"https://api.github.com/users/piwik/followers","following_url":"https://api.github.com/users/piwik/following{/other_user}","gists_url":"https://api.github.com/users/piwik/gists{/gist_id}","starred_url":"https://api.github.com/users/piwik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piwik/subscriptions","organizations_url":"https://api.github.com/users/piwik/orgs","repos_url":"https://api.github.com/users/piwik/repos","events_url":"https://api.github.com/users/piwik/events{/privacy}","received_events_url":"https://api.github.com/users/piwik/received_events","type":"Organization","site_admin":false},"repo":{"id":1548202,"name":"piwik","full_name":"piwik/piwik","owner":{"login":"piwik","id":698038,"avatar_url":"https://avatars.githubusercontent.com/u/698038?v=3","gravatar_id":"","url":"https://api.github.com/users/piwik","html_url":"https://github.com/piwik","followers_url":"https://api.github.com/users/piwik/followers","following_url":"https://api.github.com/users/piwik/following{/other_user}","gists_url":"https://api.github.com/users/piwik/gists{/gist_id}","starred_url":"https://api.github.com/users/piwik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piwik/subscriptions","organizations_url":"https://api.github.com/users/piwik/orgs","repos_url":"https://api.github.com/users/piwik/repos","events_url":"https://api.github.com/users/piwik/events{/privacy}","received_events_url":"https://api.github.com/users/piwik/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/piwik/piwik","description":"Liberating Web Analytics. Star us on Github? +1. Piwik is the leading open alternative to Google Analytics that gives you full control over your data. Piwik lets you easily collect data from websites, apps & IoT and visualise this data and extract insights. Privacy is built-in. We love Pull Requests! ","fork":false,"url":"https://api.github.com/repos/piwik/piwik","forks_url":"https://api.github.com/repos/piwik/piwik/forks","keys_url":"https://api.github.com/repos/piwik/piwik/keys{/key_id}","collaborators_url":"https://api.github.com/repos/piwik/piwik/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/piwik/piwik/teams","hooks_url":"https://api.github.com/repos/piwik/piwik/hooks","issue_events_url":"https://api.github.com/repos/piwik/piwik/issues/events{/number}","events_url":"https://api.github.com/repos/piwik/piwik/events","assignees_url":"https://api.github.com/repos/piwik/piwik/assignees{/user}","branches_url":"https://api.github.com/repos/piwik/piwik/branches{/branch}","tags_url":"https://api.github.com/repos/piwik/piwik/tags","blobs_url":"https://api.github.com/repos/piwik/piwik/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/piwik/piwik/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/piwik/piwik/git/refs{/sha}","trees_url":"https://api.github.com/repos/piwik/piwik/git/trees{/sha}","statuses_url":"https://api.github.com/repos/piwik/piwik/statuses/{sha}","languages_url":"https://api.github.com/repos/piwik/piwik/languages","stargazers_url":"https://api.github.com/repos/piwik/piwik/stargazers","contributors_url":"https://api.github.com/repos/piwik/piwik/contributors","subscribers_url":"https://api.github.com/repos/piwik/piwik/subscribers","subscription_url":"https://api.github.com/repos/piwik/piwik/subscription","commits_url":"https://api.github.com/repos/piwik/piwik/commits{/sha}","git_commits_url":"https://api.github.com/repos/piwik/piwik/git/commits{/sha}","comments_url":"https://api.github.com/repos/piwik/piwik/comments{/number}","issue_comment_url":"https://api.github.com/repos/piwik/piwik/issues/comments{/number}","contents_url":"https://api.github.com/repos/piwik/piwik/contents/{+path}","compare_url":"https://api.github.com/repos/piwik/piwik/compare/{base}...{head}","merges_url":"https://api.github.com/repos/piwik/piwik/merges","archive_url":"https://api.github.com/repos/piwik/piwik/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/piwik/piwik/downloads","issues_url":"https://api.github.com/repos/piwik/piwik/issues{/number}","pulls_url":"https://api.github.com/repos/piwik/piwik/pulls{/number}","milestones_url":"https://api.github.com/repos/piwik/piwik/milestones{/number}","notifications_url":"https://api.github.com/repos/piwik/piwik/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/piwik/piwik/labels{/name}","releases_url":"https://api.github.com/repos/piwik/piwik/releases{/id}","created_at":"2011-03-30T21:18:17Z","updated_at":"2015-04-10T02:48:29Z","pushed_at":"2015-04-10T02:59:00Z","git_url":"git://github.com/piwik/piwik.git","ssh_url":"git@github.com:piwik/piwik.git","clone_url":"https://github.com/piwik/piwik.git","svn_url":"https://github.com/piwik/piwik","homepage":"http://piwik.org/","size":284907,"stargazers_count":3688,"watchers_count":3688,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":675,"mirror_url":null,"open_issues_count":947,"forks":675,"open_issues":947,"watchers":3688,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/piwik/piwik/pulls/7644"},"html":{"href":"https://github.com/piwik/piwik/pull/7644"},"issue":{"href":"https://api.github.com/repos/piwik/piwik/issues/7644"},"comments":{"href":"https://api.github.com/repos/piwik/piwik/issues/7644/comments"},"review_comments":{"href":"https://api.github.com/repos/piwik/piwik/pulls/7644/comments"},"review_comment":{"href":"https://api.github.com/repos/piwik/piwik/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/piwik/piwik/pulls/7644/commits"},"statuses":{"href":"https://api.github.com/repos/piwik/piwik/statuses/5d57e10591a9e7e394f649cc67ea93ed14c47292"}}}}