├── .gitignore ├── src ├── Test │ ├── .DS_Store │ ├── Integration │ │ ├── DefaultConfigTest.php │ │ └── DefaultLoggerTest.php │ ├── SecurityUtilTest.php │ ├── API │ │ ├── DefaultHttpClientTest.php │ │ ├── PluginTest.php │ │ ├── ClientTest.php │ │ ├── HostTest.php │ │ ├── AbstractAPIClientTest.php │ │ └── AbstractPluginActionsTest.php │ └── Router │ │ ├── DefaultRestAPIRouterTest.php │ │ └── RequestRouterTest.php ├── API │ ├── Exception │ │ ├── CloudFlareException.php │ │ └── ZoneSettingFailException.php │ ├── HttpClientInterface.php │ ├── APIInterface.php │ ├── PluginRoutes.php │ ├── DefaultHttpClient.php │ ├── Request.php │ ├── Host.php │ ├── Plugin.php │ ├── Client.php │ ├── AbstractPluginActions.php │ └── AbstractAPIClient.php ├── Router │ ├── RouterInterface.php │ ├── RequestRouter.php │ └── DefaultRestAPIRouter.php ├── Integration │ ├── ConfigInterface.php │ ├── IntegrationInterface.php │ ├── DefaultConfig.php │ ├── DataStoreInterface.php │ ├── DefaultLogger.php │ ├── IntegrationAPIInterface.php │ └── DefaultIntegration.php ├── Utils.php ├── DNSRecord.php └── SecurityUtil.php ├── README.md ├── .travis.yml ├── phpunit.xml ├── composer.json ├── LICENSE.md ├── CHANGELOG.md └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | -------------------------------------------------------------------------------- /src/Test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-backend/master/src/Test/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Repository deprecated 2 | 3 | This repository is no longer maintained and has been merged into https://github.com/cloudflare/cloudflare-wordpress. 4 | -------------------------------------------------------------------------------- /src/API/Exception/CloudFlareException.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./src/Test/ 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/API/Exception/ZoneSettingFailException.php: -------------------------------------------------------------------------------- 1 | $value))); 14 | $this->assertEquals($value, $config->getValue($key)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Integration/IntegrationInterface.php: -------------------------------------------------------------------------------- 1 | debug(''); 13 | $this->assertTrue($returnValue); 14 | 15 | $logger = new DefaultLogger(false); 16 | $returnValue = $logger->debug(''); 17 | $this->assertNull($returnValue); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare/cloudflare-plugin-backend", 3 | "description": "A PHP backend for Cloudflare plugins.", 4 | "license": "BSD-3-Clause", 5 | "version": "2.6.0", 6 | "scripts": { 7 | "test": "vendor/bin/phpunit", 8 | "lint": "vendor/bin/phpcs -n --standard=PSR2 --extensions=php src/", 9 | "format": "vendor/bin/phpcbf --standard=PSR2 --extensions=php src/" 10 | }, 11 | "require": { 12 | "psr/log": "^1.0" 13 | }, 14 | "require-dev": { 15 | "phpunit/phpunit": "4.8.*", 16 | "squizlabs/php_codesniffer": "2.*", 17 | "guzzlehttp/guzzle": "~5.0" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "CF\\": "src/" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Integration/DefaultConfig.php: -------------------------------------------------------------------------------- 1 | config = json_decode($config, true); 15 | } 16 | 17 | /** 18 | * @param $key 19 | * 20 | * @return value or key or null 21 | */ 22 | public function getValue($key) 23 | { 24 | $value = null; 25 | if (array_key_exists($key, $this->config)) { 26 | $value = $this->config[$key]; 27 | } 28 | 29 | return $value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Integration/DataStoreInterface.php: -------------------------------------------------------------------------------- 1 | assertTrue(SecurityUtil::csrfTokenValidate($secret, $user, $token)); 13 | } 14 | 15 | public function testCSRFTokenValidateReturnsFalseForInvalidToken() 16 | { 17 | $secret = 'secret'; 18 | $user = 'user'; 19 | $timeValidUntil = time() + 86400; 20 | $token = SecurityUtil::csrfTokenGenerate($secret, $user, $timeValidUntil); 21 | $this->assertFalse(SecurityUtil::csrfTokenValidate('bad secret', $user, $token)); 22 | } 23 | 24 | public function testCSRFTokenValidateReturnsFalseForExpiredToken() 25 | { 26 | $secret = 'secret'; 27 | $user = 'user'; 28 | $timeValidUntil = time() - 1; 29 | $token = SecurityUtil::csrfTokenGenerate($secret, $user, $timeValidUntil); 30 | $this->assertFalse(SecurityUtil::csrfTokenValidate($secret, $user, $token)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Integration/DefaultLogger.php: -------------------------------------------------------------------------------- 1 | debug = $debug; 21 | } 22 | 23 | /** 24 | * Logs with an arbitrary level. 25 | * 26 | * @param mixed $level 27 | * @param string $message 28 | * @param array $context 29 | */ 30 | public function log($level, $message, array $context = array()) 31 | { 32 | return error_log(self::PREFIX.' '.strtoupper($level).': '.$message.' '. 33 | (!empty($context) ? print_r($context, true) : '')); 34 | } 35 | 36 | /** 37 | * Detailed debug information. 38 | * 39 | * @param string $message 40 | * @param array $context 41 | */ 42 | public function debug($message, array $context = array()) 43 | { 44 | if ($this->debug) { 45 | return $this->log(LogLevel::DEBUG, $message, $context); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Integration/IntegrationAPIInterface.php: -------------------------------------------------------------------------------- 1 | array( 9 | 'class' => 'CF\API\AbstractPluginActions', 10 | 'methods' => array( 11 | 'POST' => array( 12 | 'function' => 'login', 13 | ), 14 | ), 15 | ), 16 | 17 | 'plugin/:id/settings' => array( 18 | 'class' => 'CF\API\AbstractPluginActions', 19 | 'methods' => array( 20 | 'GET' => array( 21 | 'function' => 'getPluginSettings', 22 | ), 23 | ), 24 | ), 25 | 26 | 'plugin/:id/settings/:human_readable_id' => array( 27 | 'class' => 'CF\API\AbstractPluginActions', 28 | 'methods' => array( 29 | 'PATCH' => array( 30 | 'function' => 'patchPluginSettings', 31 | ), 32 | ), 33 | ), 34 | 35 | 'config' => array( 36 | 'class' => 'CF\API\AbstractPluginActions', 37 | 'methods' => array( 38 | 'GET' => array( 39 | 'function' => 'getConfig', 40 | ), 41 | ), 42 | ), 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Cloudflare. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/DNSRecord.php: -------------------------------------------------------------------------------- 1 | content; 26 | } 27 | 28 | /** 29 | * @param mixed $content 30 | */ 31 | public function setContent($content) 32 | { 33 | $this->content = $content; 34 | } 35 | 36 | /** 37 | * @return mixed 38 | */ 39 | public function getName() 40 | { 41 | return $this->name; 42 | } 43 | 44 | /** 45 | * @param mixed $name 46 | */ 47 | public function setName($name) 48 | { 49 | $this->name = $name; 50 | } 51 | 52 | /** 53 | * @return mixed 54 | */ 55 | public function getTtl() 56 | { 57 | return $this->ttl; 58 | } 59 | 60 | /** 61 | * @param mixed $ttl 62 | */ 63 | public function setTtl($ttl) 64 | { 65 | $this->ttl = $ttl; 66 | } 67 | 68 | /** 69 | * @return mixed 70 | */ 71 | public function getType() 72 | { 73 | return $this->type; 74 | } 75 | 76 | /** 77 | * @param mixed $type 78 | */ 79 | public function setType($type) 80 | { 81 | $this->type = $type; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Router/RequestRouter.php: -------------------------------------------------------------------------------- 1 | integrationContext = $integrationContext; 21 | $this->routerList = array(); 22 | } 23 | 24 | /** 25 | * @param APIInterface $client 26 | * @param $routes 27 | */ 28 | public function addRouter(APIInterface $client, $routes) 29 | { 30 | $router = new DefaultRestAPIRouter($this->integrationContext, $client, $routes); 31 | $this->routerList[$client->getAPIClientName()] = $router; 32 | } 33 | 34 | /** 35 | * @return array 36 | */ 37 | public function getRouterList() 38 | { 39 | return $this->routerList; 40 | } 41 | 42 | /** 43 | * @param $routerList 44 | */ 45 | public function setRouterList($routerList) 46 | { 47 | $this->routerList = $routerList; 48 | } 49 | 50 | /** 51 | * @param Request $request 52 | * 53 | * @return bool 54 | */ 55 | public function route(Request $request) 56 | { 57 | foreach ($this->getRouterList() as $router) { 58 | if ($router->getAPIClient()->shouldRouteRequest($request)) { 59 | return $router->route($request); 60 | } 61 | } 62 | 63 | return; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/API/DefaultHttpClient.php: -------------------------------------------------------------------------------- 1 | client = new GuzzleHttp\Client(['base_url' => $endpoint]); 22 | } 23 | 24 | /** 25 | * @param Request $request 26 | * @throws RequestException 27 | * @return Array $response 28 | */ 29 | public function send(Request $request) 30 | { 31 | $apiRequest = $this->createGuzzleRequest($request); 32 | 33 | $response = $this->client->send($apiRequest)->json(); 34 | 35 | if (json_last_error() !== JSON_ERROR_NONE) { 36 | throw new RequestException('Error decoding client API JSON', $response); 37 | } 38 | 39 | return $response; 40 | } 41 | 42 | /** 43 | * @param Request $request 44 | * @return GuzzleHttp\Message\RequestInterface $request 45 | */ 46 | public function createGuzzleRequest(Request $request) 47 | { 48 | $bodyType = (($request->getHeaders()[self::CONTENT_TYPE_KEY] === self::APPLICATION_JSON_KEY) ? 'json' : 'body'); 49 | 50 | $requestOptions = array( 51 | 'headers' => $request->getHeaders(), 52 | 'query' => $request->getParameters(), 53 | $bodyType => $request->getBody(), 54 | ); 55 | 56 | return $this->client->createRequest($request->getMethod(), $request->getUrl(), $requestOptions); 57 | } 58 | 59 | /** 60 | * @param GuzzleHttpClient $client 61 | */ 62 | public function setClient($client) 63 | { 64 | $this->client = $client; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Test/API/DefaultHttpClientTest.php: -------------------------------------------------------------------------------- 1 | mockClient = $this->getMockBuilder(GuzzleHttp\Client::class) 21 | ->disableOriginalConstructor() 22 | ->getMock(); 23 | $this->mockGuzzleRequest = $this->getMockBuilder(RequestInterface::class) 24 | ->disableOriginalConstructor() 25 | ->getMock(); 26 | $this->mockClient->method('createRequest')->willReturn($this->mockGuzzleRequest); 27 | 28 | $this->mockGuzzleResponse = $this->getMockBuilder(ResponseInterface::class) 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | $this->mockClient->method('send')->willReturn($this->mockGuzzleResponse); 32 | 33 | $this->mockRequest = $this->getMockBuilder(Request::class) 34 | ->disableOriginalConstructor() 35 | ->getMock(); 36 | 37 | $this->defaultHttpClient = new DefaultHttpClient("endpoint"); 38 | $this->defaultHttpClient->setClient($this->mockClient); 39 | } 40 | 41 | public function testSendRequestCallsGuzzleSend() 42 | { 43 | $this->mockGuzzleResponse->method('json')->willReturn(true); 44 | $this->mockClient->expects($this->once())->method('send'); 45 | 46 | $this->defaultHttpClient->send($this->mockRequest); 47 | } 48 | 49 | public function testCreateGuzzleRequestReturnsGuzzleRequest() 50 | { 51 | $this->assertInstanceOf(RequestInterface::class, $this->defaultHttpClient->createGuzzleRequest($this->mockRequest)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/API/Request.php: -------------------------------------------------------------------------------- 1 | method = strtoupper($method); 22 | $this->url = $url; 23 | $this->parameters = $parameters; 24 | $this->body = $body; 25 | } 26 | 27 | /** 28 | * @return mixed 29 | */ 30 | public function getMethod() 31 | { 32 | return $this->method; 33 | } 34 | 35 | /** 36 | * @param mixed $method 37 | */ 38 | public function setMethod($method) 39 | { 40 | $this->method = strtoupper($method); 41 | } 42 | 43 | /** 44 | * @return mixed 45 | */ 46 | public function getUrl() 47 | { 48 | return $this->url; 49 | } 50 | 51 | /** 52 | * @param mixed $url 53 | */ 54 | public function setUrl($url) 55 | { 56 | $this->url = $url; 57 | } 58 | 59 | /** 60 | * @return mixed 61 | */ 62 | public function getParameters() 63 | { 64 | return $this->parameters; 65 | } 66 | 67 | /** 68 | * @param mixed $parameters 69 | */ 70 | public function setParameters($parameters) 71 | { 72 | $this->parameters = $parameters; 73 | } 74 | 75 | /** 76 | * @return mixed 77 | */ 78 | public function getBody() 79 | { 80 | return $this->body; 81 | } 82 | 83 | /** 84 | * @param mixed $body 85 | */ 86 | public function setBody($body) 87 | { 88 | $this->body = $body; 89 | } 90 | 91 | /** 92 | * @return mixed 93 | */ 94 | public function getHeaders() 95 | { 96 | return $this->headers; 97 | } 98 | 99 | /** 100 | * @param mixed $headers 101 | */ 102 | public function setHeaders($headers) 103 | { 104 | $this->headers = $headers; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Integration/DefaultIntegration.php: -------------------------------------------------------------------------------- 1 | config = $config; 23 | $this->integrationAPI = $integrationAPI; 24 | $this->dataStore = $dataStore; 25 | $this->logger = $logger; 26 | } 27 | 28 | /** 29 | * @return ConfigInterface 30 | */ 31 | public function getConfig() 32 | { 33 | return $this->config; 34 | } 35 | 36 | /** 37 | * @param ConfigInterface $config 38 | */ 39 | public function setConfig(ConfigInterface $config) 40 | { 41 | $this->config = $config; 42 | } 43 | 44 | /** 45 | * @return integrationAPI 46 | */ 47 | public function getIntegrationAPI() 48 | { 49 | return $this->integrationAPI; 50 | } 51 | 52 | /** 53 | * @param IntegrationAPIInterface $integrationAPI 54 | */ 55 | public function setIntegrationAPI(IntegrationAPIInterface $integrationAPI) 56 | { 57 | $this->integrationAPI = $integrationAPI; 58 | } 59 | 60 | /** 61 | * @return DataStore 62 | */ 63 | public function getDataStore() 64 | { 65 | return $this->dataStore; 66 | } 67 | 68 | /** 69 | * @param DataStoreInterface $dataStore 70 | */ 71 | public function setDataStore(DataStoreInterface $dataStore) 72 | { 73 | $this->dataStore = $dataStore; 74 | } 75 | 76 | /** 77 | * @return LoggerInterface 78 | */ 79 | public function getLogger() 80 | { 81 | return $this->logger; 82 | } 83 | 84 | /** 85 | * @param LoggerInterface $logger 86 | */ 87 | public function setLogger(LoggerInterface $logger) 88 | { 89 | $this->logger = $logger; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Test/Router/DefaultRestAPIRouterTest.php: -------------------------------------------------------------------------------- 1 | mockConfig = $this->getMockBuilder('CF\Integration\DefaultConfig') 23 | ->disableOriginalConstructor() 24 | ->getMock(); 25 | $this->mockClientAPI = $this->getMockBuilder('CF\API\Client') 26 | ->disableOriginalConstructor() 27 | ->getMock(); 28 | $this->mockAPI = $this->getMockBuilder('CF\Integration\IntegrationAPIInterface') 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | $this->mockDataStore = $this->getMockBuilder('CF\Integration\DataStoreInterface') 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | $this->mockLogger = $this->getMockBuilder('CF\Integration\DefaultLogger') 35 | ->disableOriginalConstructor() 36 | ->getMock(); 37 | $this->mockIntegration = new DefaultIntegration($this->mockConfig, $this->mockAPI, $this->mockDataStore, $this->mockLogger); 38 | $this->clientV4APIRouter = new DefaultRestAPIRouter($this->mockIntegration, $this->mockClientAPI, $this->mockRoutes); 39 | } 40 | 41 | public function testGetRouteReturnsClassFunctionForValidRoute() 42 | { 43 | $routes = array( 44 | 'zones' => array( 45 | 'class' => 'testClass', 46 | 'methods' => array( 47 | 'GET' => array( 48 | 'function' => 'testFunction', 49 | ), 50 | ), 51 | ), 52 | ); 53 | $this->clientV4APIRouter->setRoutes($routes); 54 | 55 | $request = new Request('GET', 'zones', array(), array()); 56 | 57 | $response = $this->clientV4APIRouter->getRoute($request); 58 | 59 | $this->assertEquals(array( 60 | 'class' => 'testClass', 61 | 'function' => 'testFunction', 62 | ), $response); 63 | } 64 | 65 | public function testGetRouteReturnsFalseForNoRouteFound() 66 | { 67 | $request = new Request('GET', 'zones', array(), array()); 68 | $response = $this->clientV4APIRouter->getRoute($request); 69 | $this->assertFalse($response); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/SecurityUtil.php: -------------------------------------------------------------------------------- 1 | $timeValidFor) { 72 | return false; 73 | } 74 | 75 | return true; 76 | } 77 | 78 | /** 79 | * @param $data - string the data that will be hashed. 80 | * 81 | * @return string 82 | */ 83 | private static function hashFunction($data) 84 | { 85 | $hash = hash('sha512', $data); 86 | 87 | return substr($hash, 64); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/API/Host.php: -------------------------------------------------------------------------------- 1 | setUrl(self::ENDPOINT_PATH); 24 | 25 | $headers = array( 26 | self::CF_INTEGRATION_HEADER => $this->config->getValue('integrationName'), 27 | self::CF_INTEGRTATION_VERSION_HEADER => $this->config->getValue('version'), 28 | ); 29 | $request->setHeaders($headers); 30 | 31 | $body = $request->getBody(); 32 | $user_key_actions = array('zone_set', 'full_zone_set'); 33 | if (in_array(strtolower($body['act']), $user_key_actions)) { 34 | $body['user_key'] = $this->data_store->getHostAPIUserKey(); 35 | } 36 | $body['host_key'] = $this->integrationAPI->getHostAPIKey(); 37 | $request->setBody($body); 38 | 39 | return $request; 40 | } 41 | 42 | /** 43 | * @param $host_api_response 44 | * 45 | * @return bool 46 | */ 47 | public function responseOk($host_api_response) 48 | { 49 | return $host_api_response['result'] === 'success'; 50 | } 51 | 52 | /** 53 | * @param Request $request 54 | * 55 | * @return mixed 56 | */ 57 | public function getPath(Request $request) 58 | { 59 | return $request->getBody()['act']; 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | public function getEndpoint() 66 | { 67 | return self::ENDPOINT; 68 | } 69 | 70 | /** 71 | * @return string 72 | */ 73 | public function getAPIClientName() 74 | { 75 | return self::HOST_API_NAME; 76 | } 77 | 78 | /** 79 | * @param $message 80 | * 81 | * @return array 82 | */ 83 | public function createAPIError($message) 84 | { 85 | return array( 86 | 'request' => array( 87 | 'act' => '', 88 | ), 89 | 'result' => 'error', 90 | 'msg' => $message, 91 | 'err_code' => '', 92 | ); 93 | } 94 | 95 | /** 96 | * @param Request $request 97 | * 98 | * @return bool 99 | */ 100 | public function shouldRouteRequest(Request $request) 101 | { 102 | return $request->getUrl() === $this->getEndpoint(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Test/Router/RequestRouterTest.php: -------------------------------------------------------------------------------- 1 | mockConfig = $this->getMockBuilder(DefaultConfig::class) 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | $this->mockClient = $this->getMockBuilder(Client::class) 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | $this->mockAPI = $this->getMockBuilder(IntegrationAPIInterface::class) 35 | ->disableOriginalConstructor() 36 | ->getMock(); 37 | $this->mockDataStore = $this->getMockBuilder(DataStoreInterface::class) 38 | ->disableOriginalConstructor() 39 | ->getMock(); 40 | $this->mockLogger = $this->getMockBuilder(DefaultLogger::class) 41 | ->disableOriginalConstructor() 42 | ->getMock(); 43 | $this->mockIntegration = new DefaultIntegration($this->mockConfig, $this->mockAPI, $this->mockDataStore, $this->mockLogger); 44 | 45 | $this->mockRequest = $this->getMockBuilder(Request::class) 46 | ->disableOriginalConstructor() 47 | ->getMock(); 48 | 49 | $this->requestRouter = new RequestRouter($this->mockIntegration); 50 | } 51 | 52 | public function testAddRouterAddsRouter() 53 | { 54 | $clientName = "clientName"; 55 | $this->mockClient->method('getAPIClientName')->willReturn($clientName); 56 | 57 | $this->requestRouter->addRouter($this->mockClient, null); 58 | $this->assertEquals(DefaultRestAPIRouter::class, get_class($this->requestRouter->getRouterList()[$clientName])); 59 | } 60 | 61 | public function testRoutePassesValidRequestToDefaultRestAPIRouter() 62 | { 63 | $mockDefaultRestAPIRouter = $this->getMockBuilder('CF\Router\DefaultRestAPIRouter') 64 | ->disableOriginalConstructor() 65 | ->getMock(); 66 | $mockAPIClient = $this->getMockBuilder('CF\API\Client') 67 | ->disableOriginalConstructor() 68 | ->getMock(); 69 | $mockAPIClient->method('shouldRouteRequest')->willReturn(true); 70 | $mockDefaultRestAPIRouter->method('getAPIClient')->willReturn($mockAPIClient); 71 | $mockDefaultRestAPIRouter->expects($this->once())->method('route'); 72 | 73 | $this->requestRouter->setRouterList(array($mockDefaultRestAPIRouter)); 74 | 75 | $this->requestRouter->route($this->mockRequest); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/API/Plugin.php: -------------------------------------------------------------------------------- 1 | createAPIError('The url: '.$request->getUrl().' is not a valid path.'); 63 | } 64 | 65 | public function createAPISuccessResponse($result) 66 | { 67 | return array( 68 | 'success' => true, 69 | 'result' => $result, 70 | 'messages' => array(), 71 | 'errors' => array(), 72 | ); 73 | } 74 | 75 | /** 76 | * @param $pluginSettingKey 77 | * @param $value 78 | * @param $editable 79 | * @param $modified_on 80 | * 81 | * @return array 82 | */ 83 | public function createPluginSettingObject($pluginSettingKey, $value, $editable, $modified_on) 84 | { 85 | //allow null for settings that have never been set 86 | if ($modified_on !== null) { 87 | // Format ISO 8601 88 | $modified_on = date('c'); 89 | } 90 | 91 | return array( 92 | self::SETTING_ID_KEY => $pluginSettingKey, 93 | self::SETTING_VALUE_KEY => $value, 94 | self::SETTING_EDITABLE_KEY => $editable, 95 | self::SETTING_MODIFIED_DATE_KEY => $modified_on, 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Test/API/PluginTest.php: -------------------------------------------------------------------------------- 1 | mockConfig = $this->getMockBuilder('CF\Integration\DefaultConfig') 21 | ->disableOriginalConstructor() 22 | ->getMock(); 23 | $this->mockWordPressAPI = $this->getMockBuilder('CF\Integration\IntegrationAPIInterface') 24 | ->disableOriginalConstructor() 25 | ->getMock(); 26 | $this->mockDataStore = $this->getMockBuilder('CF\Integration\DataStoreInterface') 27 | ->disableOriginalConstructor() 28 | ->getMock(); 29 | $this->mockLogger = $this->getMockBuilder('CF\Integration\DefaultLogger') 30 | ->disableOriginalConstructor() 31 | ->getMock(); 32 | $this->mockRequest = $this->getMockBuilder('CF\API\Request') 33 | ->disableOriginalConstructor() 34 | ->getMock(); 35 | $this->mockDefaultIntegration = new DefaultIntegration($this->mockConfig, $this->mockWordPressAPI, $this->mockDataStore, $this->mockLogger); 36 | $this->pluginAPIClient = new Plugin($this->mockDefaultIntegration); 37 | } 38 | 39 | public function testCreateAPISuccessResponse() 40 | { 41 | $resultString = 'result'; 42 | $resultArray = array('email' => $resultString); 43 | 44 | $firstResponse = $this->pluginAPIClient->createAPISuccessResponse($resultString); 45 | $secondResponse = $this->pluginAPIClient->createAPISuccessResponse($resultArray); 46 | 47 | $this->assertTrue($firstResponse['success']); 48 | $this->assertTrue($secondResponse['success']); 49 | $this->assertEquals($resultString, $firstResponse['result']); 50 | $this->assertEquals($resultArray, $secondResponse['result']); 51 | } 52 | 53 | public function testCreateAPIErrorReturnsError() 54 | { 55 | $response = $this->pluginAPIClient->createAPIError('error Message'); 56 | 57 | $this->assertFalse($response['success']); 58 | } 59 | 60 | public function testCallAPIReturnsError() 61 | { 62 | $response = $this->pluginAPIClient->callAPI($this->mockRequest); 63 | 64 | $this->assertFalse($response['success']); 65 | } 66 | 67 | public function testCreatePluginSettingObject() 68 | { 69 | $pluginSettingKey = 'key'; 70 | $value = 'value'; 71 | $editable = false; 72 | $modifiedOn = null; 73 | 74 | $expected = array( 75 | Plugin::SETTING_ID_KEY => $pluginSettingKey, 76 | Plugin::SETTING_VALUE_KEY => $value, 77 | Plugin::SETTING_EDITABLE_KEY => $editable, 78 | Plugin::SETTING_MODIFIED_DATE_KEY => $modifiedOn, 79 | ); 80 | 81 | $result = $this->pluginAPIClient->createPluginSettingObject($pluginSettingKey, $value, $editable, $modifiedOn); 82 | 83 | $this->assertEquals($expected, $result); 84 | } 85 | 86 | public function testCreatePluginSettingObjectReturnsISO8061DateForNonNullValue() 87 | { 88 | $result = $this->pluginAPIClient->createPluginSettingObject(null, null, null, true); 89 | //DateTime() will throw an exception if $result['modified_on'] isn't a valid date 90 | $this->assertInstanceOf('\DateTime', new \DateTime($result['modified_on'])); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/API/Client.php: -------------------------------------------------------------------------------- 1 | data_store->getClientV4APIKey(); 25 | $headers = array( 26 | self::CONTENT_TYPE_KEY => self::APPLICATION_JSON_KEY, 27 | ); 28 | 29 | // Determine authentication method from key format. Global API keys are 30 | // always returned in hexadecimal format, while API Tokens are encoded 31 | // using a wider range of characters. 32 | if (strlen($key) === self::AUTH_KEY_LEN && preg_match('/^[0-9a-f]+$/', $key)) { 33 | $headers[self::X_AUTH_EMAIL] = $this->data_store->getCloudFlareEmail(); 34 | $headers[self::X_AUTH_KEY] = $key; 35 | } else { 36 | $headers[self::AUTHORIZATION] = "Bearer {$key}"; 37 | } 38 | 39 | $request->setHeaders($headers); 40 | 41 | // Remove cfCSRFToken (a custom header) to save bandwidth 42 | $body = $request->getBody(); 43 | unset($body['cfCSRFToken']); 44 | $request->setBody($body); 45 | 46 | return $request; 47 | } 48 | 49 | /** 50 | * @param $message 51 | * 52 | * @return array 53 | */ 54 | public function createAPIError($message) 55 | { 56 | $this->logger->error($message); 57 | 58 | return array( 59 | 'result' => null, 60 | 'success' => false, 61 | 'errors' => array( 62 | array( 63 | 'code' => '', 64 | 'message' => $message, 65 | ), 66 | ), 67 | 'messages' => array(), 68 | ); 69 | } 70 | 71 | /** 72 | * @param error 73 | * 74 | * @return string 75 | */ 76 | public function getErrorMessage($error) 77 | { 78 | $jsonResponse = json_decode($error->getResponse()->getBody(), true); 79 | $errorMessage = $error->getMessage(); 80 | 81 | if (count($jsonResponse['errors']) > 0) { 82 | $errorMessage = $jsonResponse['errors'][0]['message']; 83 | } 84 | 85 | return $errorMessage; 86 | } 87 | 88 | /** 89 | * @param $response 90 | * 91 | * @return bool 92 | */ 93 | public function responseOk($response) 94 | { 95 | return isset($response['success']) ? $response['success'] : false; 96 | } 97 | 98 | /** 99 | * @return string 100 | */ 101 | public function getEndpoint() 102 | { 103 | return self::ENDPOINT; 104 | } 105 | 106 | /** 107 | * @return string 108 | */ 109 | public function getAPIClientName() 110 | { 111 | return self::CLIENT_API_NAME; 112 | } 113 | 114 | /** 115 | * GET /zones/:id. 116 | * 117 | * @param $zone_tag 118 | * 119 | * @return string 120 | */ 121 | public function zoneGetDetails($zone_tag) 122 | { 123 | $request = new Request('GET', 'zones/'.$zone_tag, array(), array()); 124 | 125 | return $this->callAPI($request); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Test/API/ClientTest.php: -------------------------------------------------------------------------------- 1 | mockConfig = $this->getMockBuilder('CF\Integration\DefaultConfig') 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | $this->mockAPI = $this->getMockBuilder('CF\Integration\IntegrationAPIInterface') 23 | ->getMock(); 24 | $this->mockDataStore = $this->getMockBuilder('CF\Integration\DataStoreInterface') 25 | ->disableOriginalConstructor() 26 | ->getMock(); 27 | $this->mockLogger = $this->getMockBuilder('CF\Integration\DefaultLogger') 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | $this->mockCpanelIntegration = new DefaultIntegration($this->mockConfig, $this->mockAPI, $this->mockDataStore, $this->mockLogger); 31 | 32 | $this->mockClientAPI = new Client($this->mockCpanelIntegration); 33 | } 34 | 35 | public function testBeforeSendAddsRequestHeaders() 36 | { 37 | $apiKey = '41db178adf2ef1c82c84db6ca455457646d33'; 38 | $email = 'test@email.com'; 39 | 40 | $this->mockDataStore->method('getClientV4APIKey')->willReturn($apiKey); 41 | $this->mockDataStore->method('getCloudFlareEmail')->willReturn($email); 42 | 43 | $request = new \CF\API\Request(null, null, null, null); 44 | $beforeSendRequest = $this->mockClientAPI->beforeSend($request); 45 | 46 | $actualRequestHeaders = $beforeSendRequest->getHeaders(); 47 | $expectedRequestHeaders = array( 48 | Client::X_AUTH_KEY => $apiKey, 49 | Client::X_AUTH_EMAIL => $email, 50 | Client::CONTENT_TYPE_KEY => Client::APPLICATION_JSON_KEY, 51 | ); 52 | 53 | $this->assertEquals($expectedRequestHeaders[Client::X_AUTH_KEY], $actualRequestHeaders[Client::X_AUTH_KEY]); 54 | $this->assertEquals($expectedRequestHeaders[Client::X_AUTH_EMAIL], $actualRequestHeaders[Client::X_AUTH_EMAIL]); 55 | $this->assertEquals($expectedRequestHeaders[Client::CONTENT_TYPE_KEY], $actualRequestHeaders[Client::CONTENT_TYPE_KEY]); 56 | } 57 | 58 | public function testClientApiErrorReturnsValidStructure() 59 | { 60 | $expectedErrorResponse = array( 61 | 'result' => null, 62 | 'success' => false, 63 | 'errors' => array( 64 | array( 65 | 'code' => '', 66 | 'message' => 'Test Message', 67 | ), 68 | ), 69 | 'messages' => array(), 70 | ); 71 | $errorResponse = $this->mockClientAPI->createAPIError('Test Message'); 72 | $this->assertEquals($errorResponse, $expectedErrorResponse); 73 | } 74 | 75 | public function testResponseOkReturnsTrueForValidResponse() 76 | { 77 | $v4APIResponse = array( 78 | 'success' => true, 79 | ); 80 | 81 | $this->assertTrue($this->mockClientAPI->responseOk($v4APIResponse)); 82 | } 83 | 84 | public function testGetErrorMessageSuccess() 85 | { 86 | $errorMessage = 'I am an error message'; 87 | 88 | $error = $this->getMockBuilder('\Guzzle\Http\Exception\BadResponseException') 89 | ->disableOriginalConstructor() 90 | ->setMethods(array('getResponse', 'getBody', 'getMessage')) 91 | ->getMock(); 92 | 93 | $errorJSON = json_encode( 94 | array( 95 | 'success' => false, 96 | 'errors' => array( 97 | array( 98 | 'message' => $errorMessage, 99 | ), 100 | ), 101 | ) 102 | ); 103 | 104 | $error->expects($this->any()) 105 | ->method('getMessage') 106 | ->will($this->returnValue('Not this message')); 107 | $error->expects($this->any()) 108 | ->method('getResponse') 109 | ->will($this->returnSelf()); 110 | $error->expects($this->any()) 111 | ->method('getBody') 112 | ->will($this->returnValue($errorJSON)); 113 | 114 | $this->assertEquals($errorMessage, $this->mockClientAPI->getErrorMessage($error)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Router/DefaultRestAPIRouter.php: -------------------------------------------------------------------------------- 1 | '[0-9a-z]{32}', 24 | ':bigint_id' => '[0-9]{1,19}', 25 | ':human_readable_id' => '[-0-9a-z_]{1,120}', 26 | ':rayid' => '[0-9a-z]{16}', 27 | ':firewall_rule_id' => '[0-9a-zA-Z\\-_]{1,160}', 28 | ':file_name' => '[0-9A-Za-z_\\.\\-]{1,120}', 29 | ':uuid' => '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}', 30 | ); 31 | 32 | /** 33 | * @param IntegrationInterface $integration 34 | * @param APIInterface $api 35 | * @param $routes 36 | */ 37 | public function __construct(IntegrationInterface $integration, APIInterface $api, $routes) 38 | { 39 | $this->api = $api; 40 | $this->dataStore = $integration->getDataStore(); 41 | $this->integration = $integration; 42 | $this->integrationAPI = $integration->getIntegrationAPI(); 43 | $this->logger = $integration->getLogger(); 44 | $this->routes = $routes; 45 | } 46 | 47 | /** 48 | * @param Request $request 49 | * 50 | * @return mixed 51 | */ 52 | public function route(Request $request) 53 | { 54 | $request->setUrl($this->api->getPath($request)); 55 | 56 | $routeParameters = $this->getRoute($request); 57 | if ($routeParameters) { 58 | $class = $routeParameters['class']; 59 | $function = $routeParameters['function']; 60 | $routeClass = new $class($this->integration, $this->api, $request); 61 | 62 | return $routeClass->$function(); 63 | } else { 64 | return $this->api->callAPI($request); 65 | } 66 | } 67 | 68 | /** 69 | * @param Request $request 70 | * 71 | * @return string 72 | */ 73 | public function getPath(Request $request) 74 | { 75 | //substring of everything after the endpoint is the path 76 | return substr($request->getUrl(), strpos($request->getUrl(), $this->api->getEndpoint()) + strlen($this->api->getEndpoint())); 77 | } 78 | 79 | /** 80 | * @param Request $request 81 | * 82 | * @return array|bool 83 | */ 84 | public function getRoute(Request $request) 85 | { 86 | /* 87 | * This method allows CPanel to hook into our API calls that require Cpanel specific functionality. 88 | * Be VERY careful editing it, make sure you're code only fires for the specific API call you need to interact with. 89 | */ 90 | 91 | //Load up our routes and replace their placeholders (i.e. :id changes to [0-9a-z]{32}) 92 | foreach ($this->routes as $routeKey => $route_details_array) { 93 | //Replace placeholders in route 94 | $regex = str_replace( 95 | array_keys(static::$API_ROUTING_PLACEHOLDERS), 96 | array_values(static::$API_ROUTING_PLACEHOLDERS), 97 | $routeKey 98 | ); 99 | 100 | //Check to see if this is our route 101 | if (preg_match('#^'.$regex.'/?$#', $request->getUrl())) { 102 | if (in_array($request->getMethod(), $route_details_array['methods']) || array_key_exists( 103 | $request->getMethod(), 104 | $route_details_array['methods'] 105 | ) 106 | ) { 107 | $this->logger->debug('Route matched for '.$request->getMethod().$request->getUrl().' now using '.$route_details_array['methods'][$request->getMethod()]['function']); 108 | 109 | return array( 110 | 'class' => $route_details_array['class'], 111 | 'function' => $route_details_array['methods'][$request->getMethod()]['function'], 112 | ); 113 | } 114 | } 115 | } 116 | 117 | //if no route was found call our API normally 118 | return false; 119 | } 120 | 121 | /** 122 | * @return Client 123 | */ 124 | public function getAPIClient() 125 | { 126 | return $this->api; 127 | } 128 | 129 | /** 130 | * @param $routes 131 | */ 132 | public function setRoutes($routes) 133 | { 134 | $this->routes = $routes; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Test/API/HostTest.php: -------------------------------------------------------------------------------- 1 | mockConfig = $this->getMockBuilder('CF\Integration\DefaultConfig') 21 | ->disableOriginalConstructor() 22 | ->getMock(); 23 | $this->mockAPI = $this->getMockBuilder('CF\Integration\IntegrationAPIInterface') 24 | ->disableOriginalConstructor() 25 | ->getMock(); 26 | $this->mockDataStore = $this->getMockBuilder('CF\Integration\DataStoreInterface') 27 | ->disableOriginalConstructor() 28 | ->getMock(); 29 | $this->mockLogger = $this->getMockBuilder('CF\Integration\DefaultLogger') 30 | ->disableOriginalConstructor() 31 | ->getMock(); 32 | $this->mockCpanelIntegration = new DefaultIntegration($this->mockConfig, $this->mockAPI, $this->mockDataStore, $this->mockLogger); 33 | 34 | $this->hostAPI = new Host($this->mockCpanelIntegration); 35 | } 36 | 37 | public function testBeforeSendSetsCorrectPath() 38 | { 39 | $request = new Request(null, null, null, null); 40 | $request = $this->hostAPI->beforeSend($request); 41 | 42 | $this->assertEquals(Host::ENDPOINT_PATH, $request->getUrl()); 43 | } 44 | 45 | public function testBeforeSendSetsIntegrationHeaders() 46 | { 47 | $integrationName = 'integrationName'; 48 | $version = 'version'; 49 | 50 | $this->mockConfig->method('getValue')->will( 51 | $this->returnValueMap( 52 | array( 53 | array($integrationName, $integrationName), 54 | array($version, $version), 55 | ) 56 | ) 57 | ); 58 | 59 | $request = new Request(null, null, null, null); 60 | $request = $this->hostAPI->beforeSend($request); 61 | 62 | $requestHeaders = $request->getHeaders(); 63 | 64 | $this->assertEquals($integrationName, $requestHeaders[Host::CF_INTEGRATION_HEADER]); 65 | $this->assertEquals($version, $requestHeaders[Host::CF_INTEGRTATION_VERSION_HEADER]); 66 | } 67 | 68 | public function testBeforeSendSetsUserKeyforActZoneSet() 69 | { 70 | $userKey = 'userKey'; 71 | $this->mockDataStore->method('getHostAPIUserKey')->willReturn($userKey); 72 | 73 | $request = new Request(null, null, null, array('act' => 'zone_set')); 74 | $request = $this->hostAPI->beforeSend($request); 75 | 76 | $requestBody = $request->getBody(); 77 | 78 | $this->assertEquals($userKey, $requestBody['user_key']); 79 | } 80 | 81 | public function testBeforeSendSetsUserKeyforActFullZoneSet() 82 | { 83 | $userKey = 'userKey'; 84 | $this->mockDataStore->method('getHostAPIUserKey')->willReturn($userKey); 85 | 86 | $request = new Request(null, null, null, array('act' => 'full_zone_set')); 87 | $request = $this->hostAPI->beforeSend($request); 88 | 89 | $requestBody = $request->getBody(); 90 | 91 | $this->assertEquals($userKey, $requestBody['user_key']); 92 | } 93 | 94 | public function testBeforeSendSetsHostKey() 95 | { 96 | $hostKey = 'hostKey'; 97 | $this->mockAPI->method('getHostAPIKey')->willReturn($hostKey); 98 | 99 | $request = new Request(null, null, null, null); 100 | $request = $this->hostAPI->beforeSend($request); 101 | 102 | $requestBody = $request->getBody(); 103 | 104 | $this->assertEquals($hostKey, $requestBody['host_key']); 105 | } 106 | 107 | public function testResponseOkReturnsTrueForValidResponse() 108 | { 109 | $hostAPIResponse = array( 110 | 'result' => 'success', 111 | ); 112 | 113 | $this->assertTrue($this->hostAPI->responseOk($hostAPIResponse)); 114 | } 115 | 116 | public function testClientApiErrorReturnsValidStructure() 117 | { 118 | $message = 'message'; 119 | 120 | $errorResponse = $this->hostAPI->createAPIError($message); 121 | 122 | $this->assertEquals($message, $errorResponse['msg']); 123 | $this->assertEquals('error', $errorResponse['result']); 124 | } 125 | 126 | public function testGetPathReturnsBodyActParameter() 127 | { 128 | $act = 'act'; 129 | $request = new Request(null, null, null, array($act => $act)); 130 | $this->assertEquals($act, $this->hostAPI->getPath($request)); 131 | } 132 | 133 | public function testShouldRouteRequestReturnsTrueIfUrlsAreEqual() 134 | { 135 | $request = new Request(null, Host::ENDPOINT, null, null); 136 | $this->assertTrue($this->hostAPI->shouldRouteRequest($request)); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/Test/API/AbstractAPIClientTest.php: -------------------------------------------------------------------------------- 1 | [], 30 | 'result_info' => [ 31 | 'total_pages' => self::TOTAL_PAGES 32 | ] 33 | ]; 34 | 35 | public function setup() 36 | { 37 | $this->mockRequest = $this->getMockBuilder(Request::class) 38 | ->disableOriginalConstructor() 39 | ->getMock(); 40 | 41 | $this->mockClient = $this->getMockBuilder(HttpClientInterface::class) 42 | ->disableOriginalConstructor() 43 | ->getMock(); 44 | $this->mockConfig = $this->getMockBuilder(DefaultConfig::class) 45 | ->disableOriginalConstructor() 46 | ->getMock(); 47 | $this->mockConfig->method('getValue')->willReturn(true); 48 | $this->mockAPI = $this->getMockBuilder(IntegrationAPIInterface::class) 49 | ->getMock(); 50 | $this->mockDataStore = $this->getMockBuilder(DataStoreInterface::class) 51 | ->disableOriginalConstructor() 52 | ->getMock(); 53 | $this->mockLogger = $this->getMockBuilder(DefaultLogger::class) 54 | ->disableOriginalConstructor() 55 | ->getMock(); 56 | $this->mockIntegration = new DefaultIntegration($this->mockConfig, $this->mockAPI, $this->mockDataStore, $this->mockLogger); 57 | 58 | $this->mockAbstractAPIClient = $this->getMockBuilder(AbstractAPIClient::class) 59 | ->setConstructorArgs([$this->mockIntegration]) 60 | ->getMockForAbstractClass(); 61 | $this->mockAbstractAPIClient->setHttpClient($this->mockClient); 62 | } 63 | 64 | public function testGetPaginatedResultsRequestsAllPages() 65 | { 66 | $this->mockRequest->method('getMethod')->willReturn('GET'); 67 | $this->mockClient->expects($this->exactly((self::TOTAL_PAGES - 1)))->method('send')->willReturn([ 68 | 'result' => [] 69 | ]); 70 | $this->mockAbstractAPIClient->getPaginatedResults($this->mockRequest, self::MOCK_RESPONSE); 71 | } 72 | 73 | public function testGetPaginatedResultsOnlyExecutesForGET() 74 | { 75 | $methods = ['DELETE', 'PUT', 'PATCH', 'POST']; 76 | $this->mockClient->expects($this->never())->method('send'); 77 | 78 | foreach ($methods as $method) { 79 | $this->mockRequest = $this->getMockBuilder(Request::class) 80 | ->disableOriginalConstructor() 81 | ->getMock(); 82 | $this->mockRequest->method('getMethod')->willReturn($method); 83 | $this->mockAbstractAPIClient->getPaginatedResults($this->mockRequest, self::MOCK_RESPONSE); 84 | } 85 | } 86 | 87 | public function testGetPaginatedResultsOnlyExecutesForPagedResults() 88 | { 89 | $this->mockClient->expects($this->never())->method('send'); 90 | $this->mockAbstractAPIClient->getPaginatedResults($this->mockRequest, []); 91 | } 92 | 93 | public function testGetPathReturnsPath() 94 | { 95 | $endpoint = 'http://api.cloudflare.com/client/v4'; 96 | $path = '/zones'; 97 | $this->mockRequest->method('getUrl')->willReturn($endpoint.$path); 98 | $this->mockAbstractAPIClient->method('getEndpoint')->willReturn($endpoint); 99 | $this->assertEquals($path, $this->mockAbstractAPIClient->getPath($this->mockRequest)); 100 | } 101 | 102 | public function testShouldRouteRequestReturnsTrueForValidRequest() 103 | { 104 | $endpoint = 'http://api.cloudflare.com/client/v4'; 105 | $url = $endpoint.'/zones'; 106 | $this->mockRequest->method('getUrl')->willReturn($url); 107 | $this->mockAbstractAPIClient->method('getEndpoint')->willReturn($endpoint); 108 | $this->assertTrue($this->mockAbstractAPIClient->shouldRouteRequest($this->mockRequest)); 109 | } 110 | 111 | public function testShouldRouteRequestReturnsFalseForInvalidRequest() 112 | { 113 | $this->mockRequest->method('getUrl')->willReturn('http://api.cloudflare.com/client/v4/zones'); 114 | $this->mockAbstractAPIClient->method('getEndpoint')->willReturn('https://api.cloudflare.com/host-gw.html'); 115 | $this->assertFalse($this->mockAbstractAPIClient->shouldRouteRequest($this->mockRequest)); 116 | } 117 | 118 | public function testSendAndLogCallsLogger() 119 | { 120 | $this->mockLogger->expects($this->once())->method('error'); 121 | $this->mockAbstractAPIClient->sendAndLog($this->mockRequest); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/API/AbstractPluginActions.php: -------------------------------------------------------------------------------- 1 | api = $api; 26 | $this->config = $defaultIntegration->getConfig(); 27 | $this->integrationAPI = $defaultIntegration->getIntegrationAPI(); 28 | $this->dataStore = $defaultIntegration->getDataStore(); 29 | $this->logger = $defaultIntegration->getLogger(); 30 | $this->request = $request; 31 | 32 | $this->clientAPI = new Client($defaultIntegration); 33 | } 34 | 35 | /** 36 | * @param APIInterface $api 37 | */ 38 | public function setAPI(APIInterface $api) 39 | { 40 | $this->api = $api; 41 | } 42 | 43 | /** 44 | * @param Request $request 45 | */ 46 | public function setRequest(Request $request) 47 | { 48 | $this->request = $request; 49 | } 50 | 51 | /** 52 | * @param APIInterface $clientAPI 53 | */ 54 | public function setClientAPI(APIInterface $clientAPI) 55 | { 56 | $this->clientAPI = $clientAPI; 57 | } 58 | 59 | /** 60 | * @param DataStoreInterface $dataStore 61 | */ 62 | public function setDataStore(DataStoreInterface $dataStore) 63 | { 64 | $this->dataStore = $dataStore; 65 | } 66 | 67 | public function setLogger(\Psr\Log\LoggerInterface $logger) 68 | { 69 | $this->logger = $logger; 70 | } 71 | 72 | /** 73 | * POST /account. 74 | * 75 | * @return mixed 76 | */ 77 | public function login() 78 | { 79 | $requestBody = $this->request->getBody(); 80 | if (empty($requestBody['apiKey'])) { 81 | return $this->api->createAPIError("Missing required parameter: 'apiKey'."); 82 | } 83 | if (empty($requestBody['email'])) { 84 | return $this->api->createAPIError("Missing required parameter: 'email'."); 85 | } 86 | 87 | $isCreated = $this->dataStore->createUserDataStore($requestBody['apiKey'], $requestBody['email'], null, null); 88 | if (!$isCreated) { 89 | return $this->api->createAPIError('Unable to save user credentials'); 90 | } 91 | 92 | $params = array(); 93 | // Only Wordpress gives us access to the zone name, so check for it here 94 | if ($this->integrationAPI instanceof \CF\WordPress\WordPressAPI) { 95 | $params = array('name' => $this->integrationAPI->getOriginalDomain()); 96 | } 97 | 98 | //Make a test request to see if the API Key, email are valid 99 | $testRequest = new Request('GET', 'zones/', $params, array()); 100 | $testResponse = $this->clientAPI->callAPI($testRequest); 101 | 102 | if (!$this->clientAPI->responseOk($testResponse)) { 103 | //remove bad credentials 104 | $this->dataStore->createUserDataStore(null, null, null, null); 105 | 106 | return $this->api->createAPIError('Email address or API key invalid.'); 107 | } 108 | 109 | $response = $this->api->createAPISuccessResponse(array('email' => $requestBody['email'])); 110 | 111 | return $response; 112 | } 113 | 114 | /** 115 | * GET /plugin/:zonedId/settings. 116 | * 117 | * @return mixed 118 | */ 119 | public function getPluginSettings() 120 | { 121 | $settingsList = Plugin::getPluginSettingsKeys(); 122 | 123 | $formattedSettings = array(); 124 | foreach ($settingsList as $setting) { 125 | $value = $this->dataStore->get($setting); 126 | if ($value === null) { 127 | //setting hasn't been set yet. 128 | $value = $this->api->createPluginSettingObject($setting, null, true, null); 129 | } 130 | array_push($formattedSettings, $value); 131 | } 132 | 133 | $response = $this->api->createAPISuccessResponse( 134 | $formattedSettings 135 | ); 136 | 137 | return $response; 138 | } 139 | 140 | /** 141 | * For PATCH /plugin/:zonedId/settings/:settingId 142 | * @return mixed 143 | * @throws \Exception 144 | */ 145 | public function patchPluginSettings() 146 | { 147 | $path_array = explode('/', $this->request->getUrl()); 148 | $settingId = $path_array[3]; 149 | 150 | $body = $this->request->getBody(); 151 | $value = $body['value']; 152 | $options = $this->dataStore->set($settingId, $this->api->createPluginSettingObject($settingId, $value, true, true)); 153 | 154 | if (!isset($options)) { 155 | return $this->api->createAPIError('Unable to update plugin settings'); 156 | } 157 | 158 | if ($settingId === Plugin::SETTING_DEFAULT_SETTINGS) { 159 | try { 160 | $this->applyDefaultSettings(); 161 | } catch (\Exception $e) { 162 | if ($e instanceof Exception\CloudFlareException) { 163 | return $this->api->createAPIError($e->getMessage()); 164 | } else { 165 | throw $e; 166 | } 167 | } 168 | } 169 | 170 | $response = $this->api->createAPISuccessResponse($this->dataStore->get($settingId)); 171 | 172 | return $response; 173 | } 174 | 175 | /** 176 | * For GET /userconfig 177 | * @return mixed 178 | */ 179 | public function getConfig() 180 | { 181 | $response = $this->api->createAPISuccessResponse( 182 | [] 183 | ); 184 | 185 | return $response; 186 | } 187 | 188 | 189 | /** 190 | * Children should implement this method to apply the plugin specific default settings. 191 | * 192 | * @return mixed 193 | */ 194 | abstract public function applyDefaultSettings(); 195 | } 196 | -------------------------------------------------------------------------------- /src/API/AbstractAPIClient.php: -------------------------------------------------------------------------------- 1 | config = $integration->getConfig(); 26 | $this->data_store = $integration->getDataStore(); 27 | $this->logger = $integration->getLogger(); 28 | $this->integrationAPI = $integration->getIntegrationAPI(); 29 | } 30 | 31 | /** 32 | * @return HttpClientInterface $httpClient 33 | */ 34 | public function getHttpClient() 35 | { 36 | if ($this->httpClient === null) { 37 | $this->httpClient = new DefaultHttpClient($this->getEndpoint()); 38 | } 39 | 40 | return $this->httpClient; 41 | } 42 | /** 43 | * @param HttpClientInterface $httpClient 44 | */ 45 | public function setHttpClient(HttpClientInterface $httpClient) 46 | { 47 | $this->httpClient = $httpClient; 48 | } 49 | 50 | /** 51 | * @param Request $request 52 | * 53 | * @return array|mixed 54 | */ 55 | public function callAPI(Request $request) 56 | { 57 | try { 58 | $request = $this->beforeSend($request); 59 | 60 | $response = $this->sendAndLog($request); 61 | 62 | $response = $this->getPaginatedResults($request, $response); 63 | 64 | return $response; 65 | } catch (RequestException $e) { 66 | $errorMessage = $this->getErrorMessage($e); 67 | 68 | $this->logAPICall($this->getAPIClientName(), array( 69 | 'type' => 'request', 70 | 'method' => $request->getMethod(), 71 | 'path' => $request->getUrl(), 72 | 'headers' => $request->getHeaders(), 73 | 'params' => $request->getParameters(), 74 | 'body' => $request->getBody(), ), true); 75 | $this->logAPICall($this->getAPIClientName(), array('type' => 'response', 'code' => $e->getCode(), 'body' => $errorMessage, 'stacktrace' => $e->getTraceAsString()), true); 76 | 77 | return $this->createAPIError($errorMessage); 78 | } 79 | } 80 | 81 | /** 82 | * @param Request $request 83 | * @param [Array] $response 84 | * @return [Array] $paginatedResponse 85 | */ 86 | public function getPaginatedResults(Request $request, $response) 87 | { 88 | if (strtoupper($request->getMethod()) !== 'GET' || !isset($response['result_info']['total_pages'])) { 89 | return $response; 90 | } 91 | 92 | $mergedResponse = $response; 93 | $currentPage = 2; //$response already contains page 1 94 | $totalPages = $response['result_info']['total_pages']; 95 | 96 | while ($totalPages >= $currentPage) { 97 | $parameters = $request->getParameters(); 98 | $parameters['page'] = $currentPage; 99 | $request->setParameters($parameters); 100 | 101 | $pagedResponse = $this->sendAndLog($request); 102 | 103 | $mergedResponse['result'] = array_merge($mergedResponse['result'], $pagedResponse['result']); 104 | // Notify the frontend that pagination is taken care. 105 | $mergedResponse['result_info']['notify'] = 'Backend has taken care of pagination. Ouput is merged in results.'; 106 | $mergedResponse['result_info']['page'] = -1; 107 | $mergedResponse['result_info']['count'] = -1; 108 | 109 | $currentPage++; 110 | } 111 | return $mergedResponse; 112 | } 113 | 114 | /** 115 | * @param Request $request 116 | * @return Array $response 117 | */ 118 | public function sendAndLog(Request $request) 119 | { 120 | $response = $this->getHttpClient()->send($request); 121 | 122 | if (!$this->responseOk($response)) { 123 | $this->logAPICall($this->getAPIClientName(), array('type' => 'response', 'body' => $response), true); 124 | } 125 | 126 | return $response; 127 | } 128 | 129 | /** 130 | * @param $error 131 | * 132 | * @return string 133 | */ 134 | public function getErrorMessage($error) 135 | { 136 | return $error->getMessage(); 137 | } 138 | 139 | /** 140 | * @param string $apiName 141 | * @param array $message 142 | * @param bool $isError 143 | */ 144 | public function logAPICall($apiName, $message, $isError) 145 | { 146 | $sensitiveHeaderKeys = array( 147 | 'Authorization', 148 | 'X-Auth-Email', 149 | 'X-Auth-Key' 150 | ); 151 | 152 | $logLevel = 'error'; 153 | if ($isError === false) { 154 | $logLevel = 'debug'; 155 | } 156 | if (!is_string($message)) { 157 | foreach ($sensitiveHeaderKeys as $value) { 158 | if (!empty($message['headers'][$value])) { 159 | $message['headers'][$value] = 'REDACTED'; 160 | } 161 | } 162 | 163 | $message = print_r($message, true); 164 | } 165 | $this->logger->$logLevel('['.$apiName.'] '.$message); 166 | } 167 | 168 | /** 169 | * @param Request $request 170 | * 171 | * @return string 172 | */ 173 | public function getPath(Request $request) 174 | { 175 | //substring of everything after the endpoint is the path 176 | return substr($request->getUrl(), strpos($request->getUrl(), $this->getEndpoint()) + strlen($this->getEndpoint())); 177 | } 178 | 179 | public function shouldRouteRequest(Request $request) 180 | { 181 | return strpos($request->getUrl(), $this->getEndpoint()) !== false; 182 | } 183 | 184 | /** 185 | * @param Request $request 186 | * 187 | * @return mixed 188 | */ 189 | abstract public function beforeSend(Request $request); 190 | 191 | /** 192 | * @return mixed 193 | */ 194 | abstract public function getAPIClientName(); 195 | 196 | /** 197 | * @param $message 198 | * 199 | * @return array 200 | */ 201 | abstract public function createAPIError($message); 202 | 203 | /** 204 | * @return mixed 205 | */ 206 | abstract public function getEndpoint(); 207 | } 208 | -------------------------------------------------------------------------------- /src/Test/API/AbstractPluginActionsTest.php: -------------------------------------------------------------------------------- 1 | mockAPIClient = $this->getMockBuilder('\CF\API\Plugin') 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | $this->mockClientAPI = $this->getMockBuilder('\CF\API\Client') 23 | ->disableOriginalConstructor() 24 | ->getMock(); 25 | $this->mockDataStore = $this->getMockBuilder('\CF\Integration\DataStoreInterface') 26 | ->disableOriginalConstructor() 27 | ->getMock(); 28 | $this->mockLogger = $this->getMockBuilder('\Psr\Log\LoggerInterface') 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | $this->mockRequest = $this->getMockBuilder('\CF\API\Request') 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | $this->mockAbstractPluginActions = $this->getMockBuilder('CF\API\AbstractPluginActions') 35 | ->disableOriginalConstructor() 36 | ->getMockForAbstractClass(); 37 | $this->mockDefaultIntegration = $this->getMockBuilder('\CF\Integration\DefaultIntegration') 38 | ->disableOriginalConstructor() 39 | ->getMockForAbstractClass(); 40 | $this->mockAbstractPluginActions->setAPI($this->mockAPIClient); 41 | $this->mockAbstractPluginActions->setClientAPI($this->mockClientAPI); 42 | $this->mockAbstractPluginActions->setDataStore($this->mockDataStore); 43 | $this->mockAbstractPluginActions->setLogger($this->mockLogger); 44 | $this->mockAbstractPluginActions->setRequest($this->mockRequest); 45 | } 46 | 47 | public function testPostAccountSaveAPICredentialsReturnsErrorIfMissingApiKey() 48 | { 49 | $this->mockRequest->method('getBody')->willReturn(array( 50 | 'email' => 'email', 51 | )); 52 | $this->mockAPIClient->method('createAPIError')->willReturn(array('success' => false)); 53 | $this->mockDefaultIntegration->method('getOriginalDomain')->willReturn('name.com'); 54 | 55 | $response = $this->mockAbstractPluginActions->login(); 56 | 57 | $this->assertFalse($response['success']); 58 | } 59 | 60 | public function testPostAccountSaveAPICredentialsReturnsErrorIfMissingEmail() 61 | { 62 | $this->mockRequest->method('getBody')->willReturn(array( 63 | 'apiKey' => 'apiKey', 64 | )); 65 | $this->mockAPIClient->method('createAPIError')->willReturn(array('success' => false)); 66 | $this->mockDefaultIntegration->method('getOriginalDomain')->willReturn('name.com'); 67 | 68 | $response = $this->mockAbstractPluginActions->login(); 69 | 70 | $this->assertFalse($response['success']); 71 | } 72 | 73 | public function testGetPluginSettingsReturnsArray() 74 | { 75 | $this->mockDataStore->method('get')->willReturn(array()); 76 | $this->mockAPIClient 77 | ->expects($this->once()) 78 | ->method('createAPISuccessResponse') 79 | ->will($this->returnCallback(function ($input) { 80 | $this->assertTrue(is_array($input)); 81 | })); 82 | $this->mockAbstractPluginActions->getPluginSettings(); 83 | } 84 | 85 | public function testPatchPluginSettingsReturnsErrorForBadSetting() 86 | { 87 | $this->mockRequest->method('getUrl')->willReturn('plugin/:id/settings/nonExistentSetting'); 88 | $this->mockAPIClient->expects($this->once())->method('createAPIError'); 89 | $this->mockAbstractPluginActions->patchPluginSettings(); 90 | } 91 | 92 | public function testGetPluginSettingsHandlesSuccess() 93 | { 94 | /* 95 | * This assertion should fail as we add new settings and should be updated to reflect 96 | * count(Plugin::getPluginSettingsKeys()) 97 | */ 98 | $this->mockDataStore->method('get')->willReturn(array()); 99 | $this->mockDataStore->expects($this->exactly(6))->method('get'); 100 | $this->mockAPIClient->expects($this->once())->method('createAPISuccessResponse'); 101 | $this->mockAbstractPluginActions->getPluginSettings(); 102 | } 103 | 104 | public function testPatchPluginSettingsUpdatesSetting() 105 | { 106 | $value = 'value'; 107 | $settingId = 'settingId'; 108 | $this->mockRequest->method('getUrl')->willReturn('plugin/:zonedId/settings/'.$settingId); 109 | $this->mockRequest->method('getBody')->willReturn(array($value => $value)); 110 | $this->mockDataStore->method('set')->willReturn(true); 111 | $this->mockDataStore->expects($this->once())->method('set'); 112 | $this->mockAPIClient->expects($this->once())->method('createAPISuccessResponse'); 113 | $this->mockAbstractPluginActions->patchPluginSettings(); 114 | } 115 | 116 | public function testPatchPluginSettingsReturnsErrorIfSettingUpdateFails() 117 | { 118 | $value = 'value'; 119 | $settingId = 'settingId'; 120 | $this->mockRequest->method('getUrl')->willReturn('plugin/:zonedId/settings/'.$settingId); 121 | $this->mockRequest->method('getBody')->willReturn(array($value => $value)); 122 | $this->mockDataStore->method('set')->willReturn(null); 123 | $this->mockDataStore->expects($this->once())->method('set'); 124 | $this->mockAPIClient->expects($this->once())->method('createAPIError'); 125 | $this->mockAbstractPluginActions->patchPluginSettings(); 126 | } 127 | 128 | public function testLoginReturnsErrorIfAPIKeyOrEmailAreInvalid() 129 | { 130 | $apiKey = 'apiKey'; 131 | $email = 'email'; 132 | $this->mockRequest->method('getBody')->willReturn(array( 133 | $apiKey => $apiKey, 134 | $email => $email, 135 | )); 136 | $this->mockDataStore->method('createUserDataStore')->willReturn(true); 137 | $this->mockClientAPI->method('responseOk')->willReturn(false); 138 | $this->mockDefaultIntegration->method('getOriginalDomain')->willReturn('name.com'); 139 | 140 | $this->mockAPIClient->expects($this->once())->method('createAPIError'); 141 | $this->mockAbstractPluginActions->login(); 142 | } 143 | 144 | public function testGetPluginSettingsCallsCreatePluginSettingObjectIfDataStoreGetIsNull() 145 | { 146 | $this->mockDataStore->method('get')->willReturn(null); 147 | $this->mockAPIClient->expects($this->atLeastOnce())->method('createPluginSettingObject'); 148 | $this->mockAbstractPluginActions->getPluginSettings(); 149 | } 150 | 151 | public function testPatchPluginSettingsCallsApplyDefaultSettingsIfSettingIsDefaultSettings() 152 | { 153 | $settingId = 'default_settings'; 154 | $this->mockRequest->method('getUrl')->willReturn('plugin/:zonedId/settings/'.$settingId); 155 | $this->mockDataStore->method('set')->willReturn(true); 156 | $this->mockAbstractPluginActions->expects($this->once())->method('applyDefaultSettings'); 157 | $this->mockAbstractPluginActions->patchPluginSettings(); 158 | } 159 | 160 | public function testGetUserConfigReturnsEmptyJson() 161 | { 162 | $this->mockAPIClient->expects($this->once())->method('createAPISuccessResponse')->with([]); 163 | $this->mockAbstractPluginActions->getConfig(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [2.6.0](#2.6.0) - 2021-10-11 6 | 7 | ### Added 8 | - Add `SETTING_AUTOMATIC_PLATFORM_OPTIMIZATION_CACHE_BY_DEVICE_TYPE` setting [#53](https://github.com/cloudflare/cloudflare-plugin-backend/pull/53) 9 | 10 | ## [2.5.0](#2.5.0) - 2021-03-19 11 | 12 | ### Fixed 13 | - Sanitize sensitive HTTP headers when calling the `logAPICall` method. [#52](https://github.com/cloudflare/cloudflare-plugin-backend/pull/52) 14 | - Stop sending `cfCRSFToken` to remote API. [#41](https://github.com/cloudflare/cloudflare-plugin-backend/pull/41) 15 | 16 | ## [2.4.0](#2.4.0) - 2020-10-01 17 | ### Added 18 | - Added APO support [#48](https://github.com/cloudflare/cloudflare-plugin-backend/pull/48) 19 | 20 | ## [2.3.0](#2.3.0) - 2020-09-23 21 | ### Fixed 22 | - Fixed issue #255 Scope test api request to current zone. [#46](https://github.com/cloudflare/cloudflare-plugin-backend/pull/46) 23 | - Sanitize authentication headers. [#45](https://github.com/cloudflare/cloudflare-plugin-backend/pull/45) 24 | - Update version of Guzzle, fix issues with Client and tests. [#44](https://github.com/cloudflare/cloudflare-plugin-backend/pull/44) 25 | - Client.php: Use `self::` prefix for `AUTH_KEY_LEN` constant. [#43](https://github.com/cloudflare/cloudflare-plugin-backend/pull/43) 26 | 27 | ### Added 28 | - Added support for API Tokens. [#42](https://github.com/cloudflare/cloudflare-plugin-backend/pull/42) 29 | 30 | ## [2.2.0](#2.2.0) - 2017-07-10 31 | ### Added 32 | - Added a new route to get config. Returns an empty array by default. [#39](https://github.com/cloudflare/cloudflare-plugin-backend/pull/39) 33 | 34 | ## [2.1.1](#2.1.1) - 2017-04-05 35 | ### Changed 36 | - DefaultConfig has a default empty JSON config as the constructor argument now. [#38](https://github.com/cloudflare/cloudflare-plugin-backend/pull/38) 37 | 38 | ## [2.1.0](#2.1.0) - 2017-03-08 39 | ### Changed 40 | - `CF\API\AbstractAPIClient` depends on `CF\API\HttpClientInterface` and uses `CF\API\DefaultHttpClient` (Guzzle 5) [#37](https://github.com/cloudflare/cloudflare-plugin-backend/pull/37) 41 | 42 | ## [2.0.0](#2.0.0) - 2017-03-08 43 | ### Changed 44 | - Reverted `CF\API\AbstractAPIClient` to Guzzle 5 [#35](https://github.com/cloudflare/cloudflare-plugin-backend/pull/35) 45 | 46 | ### Fixed 47 | - API Clients must be instantiated outside `RequestRouter` now to fix DI issue in Magento [#36](https://github.com/cloudflare/cloudflare-plugin-backend/pull/36) 48 | 49 | ## [1.1.13](#1.1.13) - 2017-02-28 50 | ### Added 51 | - Travis CI [#34](https://github.com/cloudflare/cloudflare-plugin-backend/pull/34) 52 | 53 | ### Fixed 54 | - Fixed Guzzle type hints on getErrorMessage() [#33](https://github.com/cloudflare/cloudflare-plugin-backend/pull/33) 55 | 56 | ## [1.1.12](#1.1.12) - 2016-02-3 57 | ### Changed 58 | - Moved Guzzle to require-dev [#31](https://github.com/cloudflare/cloudflare-plugin-backend/pull/31) 59 | 60 | ## [1.1.11](#1.1.11) - 2016-09-27 61 | ### Fixed 62 | - Fixed bug where requests were not paginating. [#27](https://github.com/cloudflare/cloudflare-plugin-backend/pull/27) 63 | 64 | ## [1.1.10](#1.1.10) - 2016-09-12 65 | ### Fixed 66 | - Fixed method PUT not having a http body bug. [#26](https://github.com/cloudflare/cloudflare-plugin-backend/pull/26) 67 | 68 | ## [1.1.9](#1.1.9) - 2016-09-12 69 | ### Fixed 70 | - Fixed bugs in Guzzle3 and backend which didn't work with PHP 5.3. [#25](https://github.com/cloudflare/cloudflare-plugin-backend/pull/25) 71 | 72 | ## [1.1.8](#1.1.8) - 2016-09-12 73 | ### Changed 74 | - Downgraded guzzlehttp 5.0 to guzzle 3.9 to support PHP 5.3. [#23](https://github.com/cloudflare/cloudflare-plugin-backend/pull/23) 75 | 76 | ## [1.1.7](#1.1.7) - 2016-09-6 77 | ### Changed 78 | - Moved plugin settings consts from DataStore to Plugin API. [#18](https://github.com/cloudflare/cloudflare-plugin-backend/pull/18) 79 | - createPluginSettingObject() is no longer static. [#18](https://github.com/cloudflare/cloudflare-plugin-backend/pull/18) 80 | 81 | ### Fixed 82 | - Fixed patchPluginSettings() to return correct JSON structure. [#19](https://github.com/cloudflare/cloudflare-plugin-backend/pull/19) 83 | - Fixed bug where getPluginSettings was returning incorrect JSON structure. [#18](https://github.com/cloudflare/cloudflare-plugin-backend/pull/18) 84 | 85 | 86 | ## [1.1.6](#1.1.6) - 2016-09-5 87 | ### Fixed 88 | - Fixed where DataStoreInterface was not included [c3502d](https://github.com/cloudflare/cloudflare-plugin-backend/commit/c3502db2904be385e2ad0e37287085fcecbfba5f) 89 | 90 | ## [1.1.5](#1.1.5) - 2016-09-2 91 | ### Fixed 92 | - Fixed Datastore::get [bdfb9](https://github.com/cloudflare/cloudflare-plugin-backend/commit/bdfb94275bb297473cf0801b33938810c32f0cc3) 93 | 94 | ## [1.1.4](#1.1.4) - 2016-09-2 95 | ### Changed 96 | - Changed Datastore to support objects. [#15](https://github.com/cloudflare/cloudflare-plugin-backend/pull/15) 97 | - Made PageRuleLimitException shorter. [#16](https://github.com/cloudflare/cloudflare-plugin-backend/pull/16) 98 | 99 | ## [1.1.3](#1.1.3) - 2016-08-31 100 | ### Fixed 101 | - Fixed bug where CF\API\AbstractPluginActions::login() would log the user in with invalid credentials. [#14](https://github.com/cloudflare/cloudflare-plugin-backend/pull/14) 102 | 103 | ## [1.1.2](#1.1.2) - 2016-08-17 104 | ### Changed 105 | - Fixed bug in CF\API\Exception. [#13](https://github.com/cloudflare/cloudflare-plugin-backend/pull/13) 106 | 107 | ## [1.1.1](#1.1.1) - 2016-08-16 108 | ### Added 109 | - Added plugin_specific_cache_tag setting to CF\API\Plugin settings. [#12](https://github.com/cloudflare/cloudflare-plugin-backend/pull/12) 110 | 111 | ## [1.1.0](#1.1.0) - 2016-08-11 112 | ### Added 113 | - Added CF\Router\RequestRouter to consolidate duplicate request routing logic each plugin was implementing. [#8](https://github.com/cloudflare/cloudflare-plugin-backend/pull/8) 114 | 115 | ### Changed 116 | - PluginRoutes, PluginActions moved to CF\API to consolidate the Internal Plugin API logic across all plugins. [#10](https://github.com/cloudflare/cloudflare-plugin-backend/pull/10) 117 | 118 | ## [1.0.9](#1.0.9) - 2016-08-03 119 | ### Changed 120 | - Removed static type checking to support earlier php versions [ad13c1e](https://github.com/cloudflare/cloudflare-plugin-backend/commit/ad13c1ec6edeceae5a85f8912208ce2c80f4a5f2) 121 | 122 | ## [1.0.8](#1.0.8) - 2016-08-02 123 | ### Changed 124 | - Fixed error message bug [61584ca](https://github.com/cloudflare/cloudflare-plugin-backend/commit/61584ca56f8ed6ba76cb321593955e0b57f3c88d) 125 | 126 | ## [1.0.7](#1.0.7) - 2016-08-02 127 | ### Changed 128 | - Updated CHANGELOG [5e72177](https://github.com/cloudflare/cloudflare-plugin-backend/commit/5e72177aadf1c34cf75904b52bf017e7b6c6c672) 129 | 130 | ## [1.0.6](#1.0.6) - 2016-08-02 131 | ### Changed 132 | - Changed error message from always "Bad Request" to original API message [235b020](https://github.com/cloudflare/cloudflare-plugin-backend/commit/235b020ad48cf9c0d2cdcb067b34d1424f0571f6) 133 | 134 | ## [1.0.5](#1.0.5) - 2016-07-22 135 | ### Added 136 | - PI-697 added PLUGIN_SPECIFIC_CACHE consts to CF\API\Plugin [10fb134](https://github.com/cloudflare/cloudflare-plugin-backend/commit/10fb1346d81e6b7fb71abfdfb93ce12c3d55fb91) 137 | 138 | ## [1.0.4](#1.0.4) - 2016-07-15 139 | ### Added 140 | - Added setting name consts to CF\API\Plugin [70372ab](https://github.com/cloudflare/cloudflare-plugin-backend/commit/70372ab0d1e294e0e6b57799e31c8a22ed4dedf6) 141 | 142 | 143 | ## [1.0.3](#1.0.3) - 2016-06-27 144 | ### Added 145 | - Added CF\API\Plugin.php built by @thellimist to handle plugin specific API calls. [#3](https://github.com/cloudflare/cloudflare-plugin-backend/pull/3) 146 | 147 | ## [1.0.2](#1.0.2) - 2016-06-14 148 | ### Changed 149 | - CF\Integration\LoggerInterface::logAPICall() moved to CF\API\AbstractAPIClient::logAPICall(). [#2](https://github.com/cloudflare/cloudflare-plugin-backend/pull/2) 150 | 151 | ## Removed 152 | - Removed CF\Integration\LoggerInterface. [#2](https://github.com/cloudflare/cloudflare-plugin-backend/pull/2) 153 | 154 | ## [1.0.1](#1.0.1) - 2016-06-07 155 | ### Changed 156 | - CF\Integration\LoggerInterface now implements PSR-3 LoggerInterface. [#1](https://github.com/cloudflare/cloudflare-plugin-backend/pull/1) 157 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "a4872e9aa68eb14cfd39a53c12f2db0b", 8 | "packages": [ 9 | { 10 | "name": "psr/log", 11 | "version": "1.0.2", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/php-fig/log.git", 15 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 20 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | }, 26 | "type": "library", 27 | "extra": { 28 | "branch-alias": { 29 | "dev-master": "1.0.x-dev" 30 | } 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Psr\\Log\\": "Psr/Log/" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "PHP-FIG", 44 | "homepage": "http://www.php-fig.org/" 45 | } 46 | ], 47 | "description": "Common interface for logging libraries", 48 | "homepage": "https://github.com/php-fig/log", 49 | "keywords": [ 50 | "log", 51 | "psr", 52 | "psr-3" 53 | ], 54 | "time": "2016-10-10T12:19:37+00:00" 55 | } 56 | ], 57 | "packages-dev": [ 58 | { 59 | "name": "doctrine/instantiator", 60 | "version": "1.0.5", 61 | "source": { 62 | "type": "git", 63 | "url": "https://github.com/doctrine/instantiator.git", 64 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 65 | }, 66 | "dist": { 67 | "type": "zip", 68 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 69 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 70 | "shasum": "" 71 | }, 72 | "require": { 73 | "php": ">=5.3,<8.0-DEV" 74 | }, 75 | "require-dev": { 76 | "athletic/athletic": "~0.1.8", 77 | "ext-pdo": "*", 78 | "ext-phar": "*", 79 | "phpunit/phpunit": "~4.0", 80 | "squizlabs/php_codesniffer": "~2.0" 81 | }, 82 | "type": "library", 83 | "extra": { 84 | "branch-alias": { 85 | "dev-master": "1.0.x-dev" 86 | } 87 | }, 88 | "autoload": { 89 | "psr-4": { 90 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 91 | } 92 | }, 93 | "notification-url": "https://packagist.org/downloads/", 94 | "license": [ 95 | "MIT" 96 | ], 97 | "authors": [ 98 | { 99 | "name": "Marco Pivetta", 100 | "email": "ocramius@gmail.com", 101 | "homepage": "http://ocramius.github.com/" 102 | } 103 | ], 104 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 105 | "homepage": "https://github.com/doctrine/instantiator", 106 | "keywords": [ 107 | "constructor", 108 | "instantiate" 109 | ], 110 | "time": "2015-06-14T21:17:01+00:00" 111 | }, 112 | { 113 | "name": "guzzlehttp/guzzle", 114 | "version": "5.3.3", 115 | "source": { 116 | "type": "git", 117 | "url": "https://github.com/guzzle/guzzle.git", 118 | "reference": "93bbdb30d59be6cd9839495306c65f2907370eb9" 119 | }, 120 | "dist": { 121 | "type": "zip", 122 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/93bbdb30d59be6cd9839495306c65f2907370eb9", 123 | "reference": "93bbdb30d59be6cd9839495306c65f2907370eb9", 124 | "shasum": "" 125 | }, 126 | "require": { 127 | "guzzlehttp/ringphp": "^1.1", 128 | "php": ">=5.4.0", 129 | "react/promise": "^2.2" 130 | }, 131 | "require-dev": { 132 | "ext-curl": "*", 133 | "phpunit/phpunit": "^4.0" 134 | }, 135 | "type": "library", 136 | "autoload": { 137 | "psr-4": { 138 | "GuzzleHttp\\": "src/" 139 | } 140 | }, 141 | "notification-url": "https://packagist.org/downloads/", 142 | "license": [ 143 | "MIT" 144 | ], 145 | "authors": [ 146 | { 147 | "name": "Michael Dowling", 148 | "email": "mtdowling@gmail.com", 149 | "homepage": "https://github.com/mtdowling" 150 | } 151 | ], 152 | "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", 153 | "homepage": "http://guzzlephp.org/", 154 | "keywords": [ 155 | "client", 156 | "curl", 157 | "framework", 158 | "http", 159 | "http client", 160 | "rest", 161 | "web service" 162 | ], 163 | "time": "2018-07-31T13:33:10+00:00" 164 | }, 165 | { 166 | "name": "guzzlehttp/ringphp", 167 | "version": "1.1.0", 168 | "source": { 169 | "type": "git", 170 | "url": "https://github.com/guzzle/RingPHP.git", 171 | "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" 172 | }, 173 | "dist": { 174 | "type": "zip", 175 | "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", 176 | "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", 177 | "shasum": "" 178 | }, 179 | "require": { 180 | "guzzlehttp/streams": "~3.0", 181 | "php": ">=5.4.0", 182 | "react/promise": "~2.0" 183 | }, 184 | "require-dev": { 185 | "ext-curl": "*", 186 | "phpunit/phpunit": "~4.0" 187 | }, 188 | "suggest": { 189 | "ext-curl": "Guzzle will use specific adapters if cURL is present" 190 | }, 191 | "type": "library", 192 | "extra": { 193 | "branch-alias": { 194 | "dev-master": "1.1-dev" 195 | } 196 | }, 197 | "autoload": { 198 | "psr-4": { 199 | "GuzzleHttp\\Ring\\": "src/" 200 | } 201 | }, 202 | "notification-url": "https://packagist.org/downloads/", 203 | "license": [ 204 | "MIT" 205 | ], 206 | "authors": [ 207 | { 208 | "name": "Michael Dowling", 209 | "email": "mtdowling@gmail.com", 210 | "homepage": "https://github.com/mtdowling" 211 | } 212 | ], 213 | "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", 214 | "time": "2015-05-20T03:37:09+00:00" 215 | }, 216 | { 217 | "name": "guzzlehttp/streams", 218 | "version": "3.0.0", 219 | "source": { 220 | "type": "git", 221 | "url": "https://github.com/guzzle/streams.git", 222 | "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" 223 | }, 224 | "dist": { 225 | "type": "zip", 226 | "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", 227 | "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", 228 | "shasum": "" 229 | }, 230 | "require": { 231 | "php": ">=5.4.0" 232 | }, 233 | "require-dev": { 234 | "phpunit/phpunit": "~4.0" 235 | }, 236 | "type": "library", 237 | "extra": { 238 | "branch-alias": { 239 | "dev-master": "3.0-dev" 240 | } 241 | }, 242 | "autoload": { 243 | "psr-4": { 244 | "GuzzleHttp\\Stream\\": "src/" 245 | } 246 | }, 247 | "notification-url": "https://packagist.org/downloads/", 248 | "license": [ 249 | "MIT" 250 | ], 251 | "authors": [ 252 | { 253 | "name": "Michael Dowling", 254 | "email": "mtdowling@gmail.com", 255 | "homepage": "https://github.com/mtdowling" 256 | } 257 | ], 258 | "description": "Provides a simple abstraction over streams of data", 259 | "homepage": "http://guzzlephp.org/", 260 | "keywords": [ 261 | "Guzzle", 262 | "stream" 263 | ], 264 | "time": "2014-10-12T19:18:40+00:00" 265 | }, 266 | { 267 | "name": "phpdocumentor/reflection-common", 268 | "version": "1.0", 269 | "source": { 270 | "type": "git", 271 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 272 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" 273 | }, 274 | "dist": { 275 | "type": "zip", 276 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 277 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 278 | "shasum": "" 279 | }, 280 | "require": { 281 | "php": ">=5.5" 282 | }, 283 | "require-dev": { 284 | "phpunit/phpunit": "^4.6" 285 | }, 286 | "type": "library", 287 | "extra": { 288 | "branch-alias": { 289 | "dev-master": "1.0.x-dev" 290 | } 291 | }, 292 | "autoload": { 293 | "psr-4": { 294 | "phpDocumentor\\Reflection\\": [ 295 | "src" 296 | ] 297 | } 298 | }, 299 | "notification-url": "https://packagist.org/downloads/", 300 | "license": [ 301 | "MIT" 302 | ], 303 | "authors": [ 304 | { 305 | "name": "Jaap van Otterdijk", 306 | "email": "opensource@ijaap.nl" 307 | } 308 | ], 309 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 310 | "homepage": "http://www.phpdoc.org", 311 | "keywords": [ 312 | "FQSEN", 313 | "phpDocumentor", 314 | "phpdoc", 315 | "reflection", 316 | "static analysis" 317 | ], 318 | "time": "2015-12-27T11:43:31+00:00" 319 | }, 320 | { 321 | "name": "phpdocumentor/reflection-docblock", 322 | "version": "3.1.1", 323 | "source": { 324 | "type": "git", 325 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 326 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" 327 | }, 328 | "dist": { 329 | "type": "zip", 330 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", 331 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", 332 | "shasum": "" 333 | }, 334 | "require": { 335 | "php": ">=5.5", 336 | "phpdocumentor/reflection-common": "^1.0@dev", 337 | "phpdocumentor/type-resolver": "^0.2.0", 338 | "webmozart/assert": "^1.0" 339 | }, 340 | "require-dev": { 341 | "mockery/mockery": "^0.9.4", 342 | "phpunit/phpunit": "^4.4" 343 | }, 344 | "type": "library", 345 | "autoload": { 346 | "psr-4": { 347 | "phpDocumentor\\Reflection\\": [ 348 | "src/" 349 | ] 350 | } 351 | }, 352 | "notification-url": "https://packagist.org/downloads/", 353 | "license": [ 354 | "MIT" 355 | ], 356 | "authors": [ 357 | { 358 | "name": "Mike van Riel", 359 | "email": "me@mikevanriel.com" 360 | } 361 | ], 362 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 363 | "time": "2016-09-30T07:12:33+00:00" 364 | }, 365 | { 366 | "name": "phpdocumentor/type-resolver", 367 | "version": "0.2.1", 368 | "source": { 369 | "type": "git", 370 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 371 | "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" 372 | }, 373 | "dist": { 374 | "type": "zip", 375 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", 376 | "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", 377 | "shasum": "" 378 | }, 379 | "require": { 380 | "php": ">=5.5", 381 | "phpdocumentor/reflection-common": "^1.0" 382 | }, 383 | "require-dev": { 384 | "mockery/mockery": "^0.9.4", 385 | "phpunit/phpunit": "^5.2||^4.8.24" 386 | }, 387 | "type": "library", 388 | "extra": { 389 | "branch-alias": { 390 | "dev-master": "1.0.x-dev" 391 | } 392 | }, 393 | "autoload": { 394 | "psr-4": { 395 | "phpDocumentor\\Reflection\\": [ 396 | "src/" 397 | ] 398 | } 399 | }, 400 | "notification-url": "https://packagist.org/downloads/", 401 | "license": [ 402 | "MIT" 403 | ], 404 | "authors": [ 405 | { 406 | "name": "Mike van Riel", 407 | "email": "me@mikevanriel.com" 408 | } 409 | ], 410 | "time": "2016-11-25T06:54:22+00:00" 411 | }, 412 | { 413 | "name": "phpspec/prophecy", 414 | "version": "v1.7.0", 415 | "source": { 416 | "type": "git", 417 | "url": "https://github.com/phpspec/prophecy.git", 418 | "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" 419 | }, 420 | "dist": { 421 | "type": "zip", 422 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", 423 | "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", 424 | "shasum": "" 425 | }, 426 | "require": { 427 | "doctrine/instantiator": "^1.0.2", 428 | "php": "^5.3|^7.0", 429 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", 430 | "sebastian/comparator": "^1.1|^2.0", 431 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 432 | }, 433 | "require-dev": { 434 | "phpspec/phpspec": "^2.5|^3.2", 435 | "phpunit/phpunit": "^4.8 || ^5.6.5" 436 | }, 437 | "type": "library", 438 | "extra": { 439 | "branch-alias": { 440 | "dev-master": "1.6.x-dev" 441 | } 442 | }, 443 | "autoload": { 444 | "psr-0": { 445 | "Prophecy\\": "src/" 446 | } 447 | }, 448 | "notification-url": "https://packagist.org/downloads/", 449 | "license": [ 450 | "MIT" 451 | ], 452 | "authors": [ 453 | { 454 | "name": "Konstantin Kudryashov", 455 | "email": "ever.zet@gmail.com", 456 | "homepage": "http://everzet.com" 457 | }, 458 | { 459 | "name": "Marcello Duarte", 460 | "email": "marcello.duarte@gmail.com" 461 | } 462 | ], 463 | "description": "Highly opinionated mocking framework for PHP 5.3+", 464 | "homepage": "https://github.com/phpspec/prophecy", 465 | "keywords": [ 466 | "Double", 467 | "Dummy", 468 | "fake", 469 | "mock", 470 | "spy", 471 | "stub" 472 | ], 473 | "time": "2017-03-02T20:05:34+00:00" 474 | }, 475 | { 476 | "name": "phpunit/php-code-coverage", 477 | "version": "2.2.4", 478 | "source": { 479 | "type": "git", 480 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 481 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 482 | }, 483 | "dist": { 484 | "type": "zip", 485 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 486 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 487 | "shasum": "" 488 | }, 489 | "require": { 490 | "php": ">=5.3.3", 491 | "phpunit/php-file-iterator": "~1.3", 492 | "phpunit/php-text-template": "~1.2", 493 | "phpunit/php-token-stream": "~1.3", 494 | "sebastian/environment": "^1.3.2", 495 | "sebastian/version": "~1.0" 496 | }, 497 | "require-dev": { 498 | "ext-xdebug": ">=2.1.4", 499 | "phpunit/phpunit": "~4" 500 | }, 501 | "suggest": { 502 | "ext-dom": "*", 503 | "ext-xdebug": ">=2.2.1", 504 | "ext-xmlwriter": "*" 505 | }, 506 | "type": "library", 507 | "extra": { 508 | "branch-alias": { 509 | "dev-master": "2.2.x-dev" 510 | } 511 | }, 512 | "autoload": { 513 | "classmap": [ 514 | "src/" 515 | ] 516 | }, 517 | "notification-url": "https://packagist.org/downloads/", 518 | "license": [ 519 | "BSD-3-Clause" 520 | ], 521 | "authors": [ 522 | { 523 | "name": "Sebastian Bergmann", 524 | "email": "sb@sebastian-bergmann.de", 525 | "role": "lead" 526 | } 527 | ], 528 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 529 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 530 | "keywords": [ 531 | "coverage", 532 | "testing", 533 | "xunit" 534 | ], 535 | "time": "2015-10-06T15:47:00+00:00" 536 | }, 537 | { 538 | "name": "phpunit/php-file-iterator", 539 | "version": "1.4.2", 540 | "source": { 541 | "type": "git", 542 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 543 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" 544 | }, 545 | "dist": { 546 | "type": "zip", 547 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 548 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 549 | "shasum": "" 550 | }, 551 | "require": { 552 | "php": ">=5.3.3" 553 | }, 554 | "type": "library", 555 | "extra": { 556 | "branch-alias": { 557 | "dev-master": "1.4.x-dev" 558 | } 559 | }, 560 | "autoload": { 561 | "classmap": [ 562 | "src/" 563 | ] 564 | }, 565 | "notification-url": "https://packagist.org/downloads/", 566 | "license": [ 567 | "BSD-3-Clause" 568 | ], 569 | "authors": [ 570 | { 571 | "name": "Sebastian Bergmann", 572 | "email": "sb@sebastian-bergmann.de", 573 | "role": "lead" 574 | } 575 | ], 576 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 577 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 578 | "keywords": [ 579 | "filesystem", 580 | "iterator" 581 | ], 582 | "time": "2016-10-03T07:40:28+00:00" 583 | }, 584 | { 585 | "name": "phpunit/php-text-template", 586 | "version": "1.2.1", 587 | "source": { 588 | "type": "git", 589 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 590 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 591 | }, 592 | "dist": { 593 | "type": "zip", 594 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 595 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 596 | "shasum": "" 597 | }, 598 | "require": { 599 | "php": ">=5.3.3" 600 | }, 601 | "type": "library", 602 | "autoload": { 603 | "classmap": [ 604 | "src/" 605 | ] 606 | }, 607 | "notification-url": "https://packagist.org/downloads/", 608 | "license": [ 609 | "BSD-3-Clause" 610 | ], 611 | "authors": [ 612 | { 613 | "name": "Sebastian Bergmann", 614 | "email": "sebastian@phpunit.de", 615 | "role": "lead" 616 | } 617 | ], 618 | "description": "Simple template engine.", 619 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 620 | "keywords": [ 621 | "template" 622 | ], 623 | "time": "2015-06-21T13:50:34+00:00" 624 | }, 625 | { 626 | "name": "phpunit/php-timer", 627 | "version": "1.0.9", 628 | "source": { 629 | "type": "git", 630 | "url": "https://github.com/sebastianbergmann/php-timer.git", 631 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 632 | }, 633 | "dist": { 634 | "type": "zip", 635 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 636 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 637 | "shasum": "" 638 | }, 639 | "require": { 640 | "php": "^5.3.3 || ^7.0" 641 | }, 642 | "require-dev": { 643 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 644 | }, 645 | "type": "library", 646 | "extra": { 647 | "branch-alias": { 648 | "dev-master": "1.0-dev" 649 | } 650 | }, 651 | "autoload": { 652 | "classmap": [ 653 | "src/" 654 | ] 655 | }, 656 | "notification-url": "https://packagist.org/downloads/", 657 | "license": [ 658 | "BSD-3-Clause" 659 | ], 660 | "authors": [ 661 | { 662 | "name": "Sebastian Bergmann", 663 | "email": "sb@sebastian-bergmann.de", 664 | "role": "lead" 665 | } 666 | ], 667 | "description": "Utility class for timing", 668 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 669 | "keywords": [ 670 | "timer" 671 | ], 672 | "time": "2017-02-26T11:10:40+00:00" 673 | }, 674 | { 675 | "name": "phpunit/php-token-stream", 676 | "version": "1.4.11", 677 | "source": { 678 | "type": "git", 679 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 680 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" 681 | }, 682 | "dist": { 683 | "type": "zip", 684 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", 685 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", 686 | "shasum": "" 687 | }, 688 | "require": { 689 | "ext-tokenizer": "*", 690 | "php": ">=5.3.3" 691 | }, 692 | "require-dev": { 693 | "phpunit/phpunit": "~4.2" 694 | }, 695 | "type": "library", 696 | "extra": { 697 | "branch-alias": { 698 | "dev-master": "1.4-dev" 699 | } 700 | }, 701 | "autoload": { 702 | "classmap": [ 703 | "src/" 704 | ] 705 | }, 706 | "notification-url": "https://packagist.org/downloads/", 707 | "license": [ 708 | "BSD-3-Clause" 709 | ], 710 | "authors": [ 711 | { 712 | "name": "Sebastian Bergmann", 713 | "email": "sebastian@phpunit.de" 714 | } 715 | ], 716 | "description": "Wrapper around PHP's tokenizer extension.", 717 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 718 | "keywords": [ 719 | "tokenizer" 720 | ], 721 | "time": "2017-02-27T10:12:30+00:00" 722 | }, 723 | { 724 | "name": "phpunit/phpunit", 725 | "version": "4.8.35", 726 | "source": { 727 | "type": "git", 728 | "url": "https://github.com/sebastianbergmann/phpunit.git", 729 | "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87" 730 | }, 731 | "dist": { 732 | "type": "zip", 733 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/791b1a67c25af50e230f841ee7a9c6eba507dc87", 734 | "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87", 735 | "shasum": "" 736 | }, 737 | "require": { 738 | "ext-dom": "*", 739 | "ext-json": "*", 740 | "ext-pcre": "*", 741 | "ext-reflection": "*", 742 | "ext-spl": "*", 743 | "php": ">=5.3.3", 744 | "phpspec/prophecy": "^1.3.1", 745 | "phpunit/php-code-coverage": "~2.1", 746 | "phpunit/php-file-iterator": "~1.4", 747 | "phpunit/php-text-template": "~1.2", 748 | "phpunit/php-timer": "^1.0.6", 749 | "phpunit/phpunit-mock-objects": "~2.3", 750 | "sebastian/comparator": "~1.2.2", 751 | "sebastian/diff": "~1.2", 752 | "sebastian/environment": "~1.3", 753 | "sebastian/exporter": "~1.2", 754 | "sebastian/global-state": "~1.0", 755 | "sebastian/version": "~1.0", 756 | "symfony/yaml": "~2.1|~3.0" 757 | }, 758 | "suggest": { 759 | "phpunit/php-invoker": "~1.1" 760 | }, 761 | "bin": [ 762 | "phpunit" 763 | ], 764 | "type": "library", 765 | "extra": { 766 | "branch-alias": { 767 | "dev-master": "4.8.x-dev" 768 | } 769 | }, 770 | "autoload": { 771 | "classmap": [ 772 | "src/" 773 | ] 774 | }, 775 | "notification-url": "https://packagist.org/downloads/", 776 | "license": [ 777 | "BSD-3-Clause" 778 | ], 779 | "authors": [ 780 | { 781 | "name": "Sebastian Bergmann", 782 | "email": "sebastian@phpunit.de", 783 | "role": "lead" 784 | } 785 | ], 786 | "description": "The PHP Unit Testing framework.", 787 | "homepage": "https://phpunit.de/", 788 | "keywords": [ 789 | "phpunit", 790 | "testing", 791 | "xunit" 792 | ], 793 | "time": "2017-02-06T05:18:07+00:00" 794 | }, 795 | { 796 | "name": "phpunit/phpunit-mock-objects", 797 | "version": "2.3.8", 798 | "source": { 799 | "type": "git", 800 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 801 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 802 | }, 803 | "dist": { 804 | "type": "zip", 805 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 806 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 807 | "shasum": "" 808 | }, 809 | "require": { 810 | "doctrine/instantiator": "^1.0.2", 811 | "php": ">=5.3.3", 812 | "phpunit/php-text-template": "~1.2", 813 | "sebastian/exporter": "~1.2" 814 | }, 815 | "require-dev": { 816 | "phpunit/phpunit": "~4.4" 817 | }, 818 | "suggest": { 819 | "ext-soap": "*" 820 | }, 821 | "type": "library", 822 | "extra": { 823 | "branch-alias": { 824 | "dev-master": "2.3.x-dev" 825 | } 826 | }, 827 | "autoload": { 828 | "classmap": [ 829 | "src/" 830 | ] 831 | }, 832 | "notification-url": "https://packagist.org/downloads/", 833 | "license": [ 834 | "BSD-3-Clause" 835 | ], 836 | "authors": [ 837 | { 838 | "name": "Sebastian Bergmann", 839 | "email": "sb@sebastian-bergmann.de", 840 | "role": "lead" 841 | } 842 | ], 843 | "description": "Mock Object library for PHPUnit", 844 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 845 | "keywords": [ 846 | "mock", 847 | "xunit" 848 | ], 849 | "time": "2015-10-02T06:51:40+00:00" 850 | }, 851 | { 852 | "name": "react/promise", 853 | "version": "v2.5.0", 854 | "source": { 855 | "type": "git", 856 | "url": "https://github.com/reactphp/promise.git", 857 | "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db" 858 | }, 859 | "dist": { 860 | "type": "zip", 861 | "url": "https://api.github.com/repos/reactphp/promise/zipball/2760f3898b7e931aa71153852dcd48a75c9b95db", 862 | "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db", 863 | "shasum": "" 864 | }, 865 | "require": { 866 | "php": ">=5.4.0" 867 | }, 868 | "type": "library", 869 | "autoload": { 870 | "psr-4": { 871 | "React\\Promise\\": "src/" 872 | }, 873 | "files": [ 874 | "src/functions_include.php" 875 | ] 876 | }, 877 | "notification-url": "https://packagist.org/downloads/", 878 | "license": [ 879 | "MIT" 880 | ], 881 | "authors": [ 882 | { 883 | "name": "Jan Sorgalla", 884 | "email": "jsorgalla@gmail.com" 885 | } 886 | ], 887 | "description": "A lightweight implementation of CommonJS Promises/A for PHP", 888 | "keywords": [ 889 | "promise", 890 | "promises" 891 | ], 892 | "time": "2016-12-22T14:09:01+00:00" 893 | }, 894 | { 895 | "name": "sebastian/comparator", 896 | "version": "1.2.4", 897 | "source": { 898 | "type": "git", 899 | "url": "https://github.com/sebastianbergmann/comparator.git", 900 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" 901 | }, 902 | "dist": { 903 | "type": "zip", 904 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 905 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 906 | "shasum": "" 907 | }, 908 | "require": { 909 | "php": ">=5.3.3", 910 | "sebastian/diff": "~1.2", 911 | "sebastian/exporter": "~1.2 || ~2.0" 912 | }, 913 | "require-dev": { 914 | "phpunit/phpunit": "~4.4" 915 | }, 916 | "type": "library", 917 | "extra": { 918 | "branch-alias": { 919 | "dev-master": "1.2.x-dev" 920 | } 921 | }, 922 | "autoload": { 923 | "classmap": [ 924 | "src/" 925 | ] 926 | }, 927 | "notification-url": "https://packagist.org/downloads/", 928 | "license": [ 929 | "BSD-3-Clause" 930 | ], 931 | "authors": [ 932 | { 933 | "name": "Jeff Welch", 934 | "email": "whatthejeff@gmail.com" 935 | }, 936 | { 937 | "name": "Volker Dusch", 938 | "email": "github@wallbash.com" 939 | }, 940 | { 941 | "name": "Bernhard Schussek", 942 | "email": "bschussek@2bepublished.at" 943 | }, 944 | { 945 | "name": "Sebastian Bergmann", 946 | "email": "sebastian@phpunit.de" 947 | } 948 | ], 949 | "description": "Provides the functionality to compare PHP values for equality", 950 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 951 | "keywords": [ 952 | "comparator", 953 | "compare", 954 | "equality" 955 | ], 956 | "time": "2017-01-29T09:50:25+00:00" 957 | }, 958 | { 959 | "name": "sebastian/diff", 960 | "version": "1.4.1", 961 | "source": { 962 | "type": "git", 963 | "url": "https://github.com/sebastianbergmann/diff.git", 964 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 965 | }, 966 | "dist": { 967 | "type": "zip", 968 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 969 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 970 | "shasum": "" 971 | }, 972 | "require": { 973 | "php": ">=5.3.3" 974 | }, 975 | "require-dev": { 976 | "phpunit/phpunit": "~4.8" 977 | }, 978 | "type": "library", 979 | "extra": { 980 | "branch-alias": { 981 | "dev-master": "1.4-dev" 982 | } 983 | }, 984 | "autoload": { 985 | "classmap": [ 986 | "src/" 987 | ] 988 | }, 989 | "notification-url": "https://packagist.org/downloads/", 990 | "license": [ 991 | "BSD-3-Clause" 992 | ], 993 | "authors": [ 994 | { 995 | "name": "Kore Nordmann", 996 | "email": "mail@kore-nordmann.de" 997 | }, 998 | { 999 | "name": "Sebastian Bergmann", 1000 | "email": "sebastian@phpunit.de" 1001 | } 1002 | ], 1003 | "description": "Diff implementation", 1004 | "homepage": "https://github.com/sebastianbergmann/diff", 1005 | "keywords": [ 1006 | "diff" 1007 | ], 1008 | "time": "2015-12-08T07:14:41+00:00" 1009 | }, 1010 | { 1011 | "name": "sebastian/environment", 1012 | "version": "1.3.8", 1013 | "source": { 1014 | "type": "git", 1015 | "url": "https://github.com/sebastianbergmann/environment.git", 1016 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" 1017 | }, 1018 | "dist": { 1019 | "type": "zip", 1020 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", 1021 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", 1022 | "shasum": "" 1023 | }, 1024 | "require": { 1025 | "php": "^5.3.3 || ^7.0" 1026 | }, 1027 | "require-dev": { 1028 | "phpunit/phpunit": "^4.8 || ^5.0" 1029 | }, 1030 | "type": "library", 1031 | "extra": { 1032 | "branch-alias": { 1033 | "dev-master": "1.3.x-dev" 1034 | } 1035 | }, 1036 | "autoload": { 1037 | "classmap": [ 1038 | "src/" 1039 | ] 1040 | }, 1041 | "notification-url": "https://packagist.org/downloads/", 1042 | "license": [ 1043 | "BSD-3-Clause" 1044 | ], 1045 | "authors": [ 1046 | { 1047 | "name": "Sebastian Bergmann", 1048 | "email": "sebastian@phpunit.de" 1049 | } 1050 | ], 1051 | "description": "Provides functionality to handle HHVM/PHP environments", 1052 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1053 | "keywords": [ 1054 | "Xdebug", 1055 | "environment", 1056 | "hhvm" 1057 | ], 1058 | "time": "2016-08-18T05:49:44+00:00" 1059 | }, 1060 | { 1061 | "name": "sebastian/exporter", 1062 | "version": "1.2.2", 1063 | "source": { 1064 | "type": "git", 1065 | "url": "https://github.com/sebastianbergmann/exporter.git", 1066 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" 1067 | }, 1068 | "dist": { 1069 | "type": "zip", 1070 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", 1071 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", 1072 | "shasum": "" 1073 | }, 1074 | "require": { 1075 | "php": ">=5.3.3", 1076 | "sebastian/recursion-context": "~1.0" 1077 | }, 1078 | "require-dev": { 1079 | "ext-mbstring": "*", 1080 | "phpunit/phpunit": "~4.4" 1081 | }, 1082 | "type": "library", 1083 | "extra": { 1084 | "branch-alias": { 1085 | "dev-master": "1.3.x-dev" 1086 | } 1087 | }, 1088 | "autoload": { 1089 | "classmap": [ 1090 | "src/" 1091 | ] 1092 | }, 1093 | "notification-url": "https://packagist.org/downloads/", 1094 | "license": [ 1095 | "BSD-3-Clause" 1096 | ], 1097 | "authors": [ 1098 | { 1099 | "name": "Jeff Welch", 1100 | "email": "whatthejeff@gmail.com" 1101 | }, 1102 | { 1103 | "name": "Volker Dusch", 1104 | "email": "github@wallbash.com" 1105 | }, 1106 | { 1107 | "name": "Bernhard Schussek", 1108 | "email": "bschussek@2bepublished.at" 1109 | }, 1110 | { 1111 | "name": "Sebastian Bergmann", 1112 | "email": "sebastian@phpunit.de" 1113 | }, 1114 | { 1115 | "name": "Adam Harvey", 1116 | "email": "aharvey@php.net" 1117 | } 1118 | ], 1119 | "description": "Provides the functionality to export PHP variables for visualization", 1120 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1121 | "keywords": [ 1122 | "export", 1123 | "exporter" 1124 | ], 1125 | "time": "2016-06-17T09:04:28+00:00" 1126 | }, 1127 | { 1128 | "name": "sebastian/global-state", 1129 | "version": "1.1.1", 1130 | "source": { 1131 | "type": "git", 1132 | "url": "https://github.com/sebastianbergmann/global-state.git", 1133 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1134 | }, 1135 | "dist": { 1136 | "type": "zip", 1137 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1138 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1139 | "shasum": "" 1140 | }, 1141 | "require": { 1142 | "php": ">=5.3.3" 1143 | }, 1144 | "require-dev": { 1145 | "phpunit/phpunit": "~4.2" 1146 | }, 1147 | "suggest": { 1148 | "ext-uopz": "*" 1149 | }, 1150 | "type": "library", 1151 | "extra": { 1152 | "branch-alias": { 1153 | "dev-master": "1.0-dev" 1154 | } 1155 | }, 1156 | "autoload": { 1157 | "classmap": [ 1158 | "src/" 1159 | ] 1160 | }, 1161 | "notification-url": "https://packagist.org/downloads/", 1162 | "license": [ 1163 | "BSD-3-Clause" 1164 | ], 1165 | "authors": [ 1166 | { 1167 | "name": "Sebastian Bergmann", 1168 | "email": "sebastian@phpunit.de" 1169 | } 1170 | ], 1171 | "description": "Snapshotting of global state", 1172 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1173 | "keywords": [ 1174 | "global state" 1175 | ], 1176 | "time": "2015-10-12T03:26:01+00:00" 1177 | }, 1178 | { 1179 | "name": "sebastian/recursion-context", 1180 | "version": "1.0.5", 1181 | "source": { 1182 | "type": "git", 1183 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1184 | "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" 1185 | }, 1186 | "dist": { 1187 | "type": "zip", 1188 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", 1189 | "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", 1190 | "shasum": "" 1191 | }, 1192 | "require": { 1193 | "php": ">=5.3.3" 1194 | }, 1195 | "require-dev": { 1196 | "phpunit/phpunit": "~4.4" 1197 | }, 1198 | "type": "library", 1199 | "extra": { 1200 | "branch-alias": { 1201 | "dev-master": "1.0.x-dev" 1202 | } 1203 | }, 1204 | "autoload": { 1205 | "classmap": [ 1206 | "src/" 1207 | ] 1208 | }, 1209 | "notification-url": "https://packagist.org/downloads/", 1210 | "license": [ 1211 | "BSD-3-Clause" 1212 | ], 1213 | "authors": [ 1214 | { 1215 | "name": "Jeff Welch", 1216 | "email": "whatthejeff@gmail.com" 1217 | }, 1218 | { 1219 | "name": "Sebastian Bergmann", 1220 | "email": "sebastian@phpunit.de" 1221 | }, 1222 | { 1223 | "name": "Adam Harvey", 1224 | "email": "aharvey@php.net" 1225 | } 1226 | ], 1227 | "description": "Provides functionality to recursively process PHP variables", 1228 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1229 | "time": "2016-10-03T07:41:43+00:00" 1230 | }, 1231 | { 1232 | "name": "sebastian/version", 1233 | "version": "1.0.6", 1234 | "source": { 1235 | "type": "git", 1236 | "url": "https://github.com/sebastianbergmann/version.git", 1237 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1238 | }, 1239 | "dist": { 1240 | "type": "zip", 1241 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1242 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1243 | "shasum": "" 1244 | }, 1245 | "type": "library", 1246 | "autoload": { 1247 | "classmap": [ 1248 | "src/" 1249 | ] 1250 | }, 1251 | "notification-url": "https://packagist.org/downloads/", 1252 | "license": [ 1253 | "BSD-3-Clause" 1254 | ], 1255 | "authors": [ 1256 | { 1257 | "name": "Sebastian Bergmann", 1258 | "email": "sebastian@phpunit.de", 1259 | "role": "lead" 1260 | } 1261 | ], 1262 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1263 | "homepage": "https://github.com/sebastianbergmann/version", 1264 | "time": "2015-06-21T13:59:46+00:00" 1265 | }, 1266 | { 1267 | "name": "squizlabs/php_codesniffer", 1268 | "version": "2.8.1", 1269 | "source": { 1270 | "type": "git", 1271 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 1272 | "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" 1273 | }, 1274 | "dist": { 1275 | "type": "zip", 1276 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", 1277 | "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", 1278 | "shasum": "" 1279 | }, 1280 | "require": { 1281 | "ext-simplexml": "*", 1282 | "ext-tokenizer": "*", 1283 | "ext-xmlwriter": "*", 1284 | "php": ">=5.1.2" 1285 | }, 1286 | "require-dev": { 1287 | "phpunit/phpunit": "~4.0" 1288 | }, 1289 | "bin": [ 1290 | "scripts/phpcs", 1291 | "scripts/phpcbf" 1292 | ], 1293 | "type": "library", 1294 | "extra": { 1295 | "branch-alias": { 1296 | "dev-master": "2.x-dev" 1297 | } 1298 | }, 1299 | "autoload": { 1300 | "classmap": [ 1301 | "CodeSniffer.php", 1302 | "CodeSniffer/CLI.php", 1303 | "CodeSniffer/Exception.php", 1304 | "CodeSniffer/File.php", 1305 | "CodeSniffer/Fixer.php", 1306 | "CodeSniffer/Report.php", 1307 | "CodeSniffer/Reporting.php", 1308 | "CodeSniffer/Sniff.php", 1309 | "CodeSniffer/Tokens.php", 1310 | "CodeSniffer/Reports/", 1311 | "CodeSniffer/Tokenizers/", 1312 | "CodeSniffer/DocGenerators/", 1313 | "CodeSniffer/Standards/AbstractPatternSniff.php", 1314 | "CodeSniffer/Standards/AbstractScopeSniff.php", 1315 | "CodeSniffer/Standards/AbstractVariableSniff.php", 1316 | "CodeSniffer/Standards/IncorrectPatternException.php", 1317 | "CodeSniffer/Standards/Generic/Sniffs/", 1318 | "CodeSniffer/Standards/MySource/Sniffs/", 1319 | "CodeSniffer/Standards/PEAR/Sniffs/", 1320 | "CodeSniffer/Standards/PSR1/Sniffs/", 1321 | "CodeSniffer/Standards/PSR2/Sniffs/", 1322 | "CodeSniffer/Standards/Squiz/Sniffs/", 1323 | "CodeSniffer/Standards/Zend/Sniffs/" 1324 | ] 1325 | }, 1326 | "notification-url": "https://packagist.org/downloads/", 1327 | "license": [ 1328 | "BSD-3-Clause" 1329 | ], 1330 | "authors": [ 1331 | { 1332 | "name": "Greg Sherwood", 1333 | "role": "lead" 1334 | } 1335 | ], 1336 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 1337 | "homepage": "http://www.squizlabs.com/php-codesniffer", 1338 | "keywords": [ 1339 | "phpcs", 1340 | "standards" 1341 | ], 1342 | "time": "2017-03-01T22:17:45+00:00" 1343 | }, 1344 | { 1345 | "name": "symfony/yaml", 1346 | "version": "v3.2.4", 1347 | "source": { 1348 | "type": "git", 1349 | "url": "https://github.com/symfony/yaml.git", 1350 | "reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8" 1351 | }, 1352 | "dist": { 1353 | "type": "zip", 1354 | "url": "https://api.github.com/repos/symfony/yaml/zipball/9724c684646fcb5387d579b4bfaa63ee0b0c64c8", 1355 | "reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8", 1356 | "shasum": "" 1357 | }, 1358 | "require": { 1359 | "php": ">=5.5.9" 1360 | }, 1361 | "require-dev": { 1362 | "symfony/console": "~2.8|~3.0" 1363 | }, 1364 | "suggest": { 1365 | "symfony/console": "For validating YAML files using the lint command" 1366 | }, 1367 | "type": "library", 1368 | "extra": { 1369 | "branch-alias": { 1370 | "dev-master": "3.2-dev" 1371 | } 1372 | }, 1373 | "autoload": { 1374 | "psr-4": { 1375 | "Symfony\\Component\\Yaml\\": "" 1376 | }, 1377 | "exclude-from-classmap": [ 1378 | "/Tests/" 1379 | ] 1380 | }, 1381 | "notification-url": "https://packagist.org/downloads/", 1382 | "license": [ 1383 | "MIT" 1384 | ], 1385 | "authors": [ 1386 | { 1387 | "name": "Fabien Potencier", 1388 | "email": "fabien@symfony.com" 1389 | }, 1390 | { 1391 | "name": "Symfony Community", 1392 | "homepage": "https://symfony.com/contributors" 1393 | } 1394 | ], 1395 | "description": "Symfony Yaml Component", 1396 | "homepage": "https://symfony.com", 1397 | "time": "2017-02-16T22:46:52+00:00" 1398 | }, 1399 | { 1400 | "name": "webmozart/assert", 1401 | "version": "1.2.0", 1402 | "source": { 1403 | "type": "git", 1404 | "url": "https://github.com/webmozart/assert.git", 1405 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" 1406 | }, 1407 | "dist": { 1408 | "type": "zip", 1409 | "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", 1410 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", 1411 | "shasum": "" 1412 | }, 1413 | "require": { 1414 | "php": "^5.3.3 || ^7.0" 1415 | }, 1416 | "require-dev": { 1417 | "phpunit/phpunit": "^4.6", 1418 | "sebastian/version": "^1.0.1" 1419 | }, 1420 | "type": "library", 1421 | "extra": { 1422 | "branch-alias": { 1423 | "dev-master": "1.3-dev" 1424 | } 1425 | }, 1426 | "autoload": { 1427 | "psr-4": { 1428 | "Webmozart\\Assert\\": "src/" 1429 | } 1430 | }, 1431 | "notification-url": "https://packagist.org/downloads/", 1432 | "license": [ 1433 | "MIT" 1434 | ], 1435 | "authors": [ 1436 | { 1437 | "name": "Bernhard Schussek", 1438 | "email": "bschussek@gmail.com" 1439 | } 1440 | ], 1441 | "description": "Assertions to validate method input/output with nice error messages.", 1442 | "keywords": [ 1443 | "assert", 1444 | "check", 1445 | "validate" 1446 | ], 1447 | "time": "2016-11-23T20:04:58+00:00" 1448 | } 1449 | ], 1450 | "aliases": [], 1451 | "minimum-stability": "stable", 1452 | "stability-flags": [], 1453 | "prefer-stable": false, 1454 | "prefer-lowest": false, 1455 | "platform": [], 1456 | "platform-dev": [] 1457 | } 1458 | --------------------------------------------------------------------------------