├── .gitignore ├── README.md ├── composer.json ├── examples ├── create-infographic.php ├── list-infographics.php └── list-themes.php ├── src └── Infogram │ ├── BaseString.php │ ├── HttpTransport.php │ ├── InfogramRequest.php │ ├── InfogramResponse.php │ ├── InfogramSession.php │ ├── Request.php │ ├── RequestSigningSession.php │ ├── Response.php │ ├── SimpleRequest.php │ ├── SimpleResponse.php │ └── Transport.php └── tests └── Infogram ├── BaseStringTest.php ├── InfogramRequestTest.php ├── RequestSigningSessionTest.php └── SimpleResponseTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | composer.lock 3 | vendor -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Infogram-PHP 2 | 3 | This library provides an API to create and update infographics on Infogr.am 4 | 5 | ## Installation 6 | 7 | The recommended way to install Infogram-PHP is via [Composer](https://getcomposer.org/). Composer is a tool for dependency management in PHP. It allows you to declare the dependent libraries your project needs and it will install them in your project for you. 8 | 9 | ```shell 10 | # Install Composer 11 | curl -sS https://getcomposer.org/installer | php 12 | ``` 13 | 14 | Add the following dependency to the `require` section of your project's `composer.json`: 15 | 16 | ```json 17 | "require": { 18 | "infogram/infogram": "1.0.*" 19 | } 20 | ``` 21 | Execute command line `composer update` in your project's root 22 | 23 | ### API Keys 24 | You're going to need API keys, the public key and the secret (or private) key, to be able to access infogr.am API service. To find your keys, log in to Infogr.am, find the [account settings](https://infogr.am/app/#/settings) on the left panel and look for the API credentials. Keep your secret key secret, don't send it to anyone and do not pass it to any service directly. 25 | 26 | The public key is used to indentify the API account the code is accessing with and the secret key is used to sign every request, i.e., add additional parameter to the request query string or body. 27 | 28 | ## Usage 29 | ### Making a Request 30 | For making a request, two Infogram-PHP classes are essential: `Infogram\RequestSigningSession` and `Infogram\InfogramRequest`. The former is responsible for HTTP request signing using the provided API secret key and the latter performs HTTP requests to the Infogr.am API service. 31 | 32 | TODO: add reference to HTTP API documentation 33 | 34 | ```php 35 | use Infogram\InfogramRequest; 36 | use Infogram\RequestSigningSession; 37 | //... 38 | $consumerKey = 'Your public key'; 39 | $consumerSecret = 'Your secret key'; 40 | $session = new RequestSigningSession($consumerKey, $consumerSecret); 41 | $request = new InfogramRequest($session, 'GET', 'themes'); 42 | $response = $request->execute(); 43 | ``` 44 | 45 | ### Using a Response 46 | Method `Infogram\InfogramRequest::execute` returns an instance of class `Infogram\InfogramResponse` or `null` if Infogr.am API server cannot be accessed. 47 | 48 | `Infogram\InfogramResponse` contains (1) HTTP status code from last API request, (2) HTTP response body, (3) HTTP response headers 49 | 50 | ```php 51 | $content = array( 52 | array( 53 | 'type' => 'h1', 54 | 'text' => 'hello' 55 | ) 56 | ); 57 | $session = new RequestSigningSession($consumerKey, $consumerSecret); 58 | $request = new InfogramRequest($session, 'POST', 'infographics', array('content' => $content)); 59 | $response = $request->execute(); 60 | if (! $response) { 61 | die("Could not contact Infogr.am API server\n"); 62 | } 63 | if (! $response->isOK()) { 64 | die('Error executing API request: ' . $response->getBody() . "\n"); 65 | } 66 | echo 'Created new infographic with ID: ' . $response->getHeader('X-Infogram-Id') . "\n"; 67 | ``` 68 | 69 | On successful request (`$response->isOK() == true`), response's method `getBody` returns either a string or array (converted from a JSON string) if applicable. 70 | On error (`! $response->isOK()`), `getBody` returns string which contains the error message if there is any. 71 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infogram/infogram", 3 | "description": "PHP client for infogr.am REST API", 4 | "keywords": ["infogram", "infogr.am"], 5 | "homepage": "https://github.com/infogram/infogram-php", 6 | "licence": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Mārtiņš Barinskis", 10 | "email": "mb@infogr.am", 11 | "role": "Developer" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.3", 16 | "rmccue/requests": ">=1.0" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "4.3.*", 20 | "mockery/mockery": "0.9.*" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Infogram\\": "src/Infogram" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/create-infographic.php: -------------------------------------------------------------------------------- 1 | -s \n" . 15 | "php create-infographic.php --key= --secret=\n"); 16 | } 17 | 18 | $consumerKey = array_key_exists('k', $options) ? $options['k'] : $options['key']; 19 | $consumerSecret = array_key_exists('s', $options) ? $options['s'] : $options['secret']; 20 | 21 | $baseUrl = null; 22 | if (array_key_exists('b', $options)) { 23 | $baseUrl = $options['b']; 24 | } 25 | else if (array_key_exists('base-url', $options)) { 26 | $baseUrl = $options['base-url']; 27 | } 28 | 29 | $content = array( 30 | array( 31 | 'type' => 'h1', 32 | 'text' => 'Testing PHP API client' 33 | ), 34 | array( 35 | 'type' => 'body', 36 | 'text'=> 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eu porttitor sapien. Donec hendrerit, mi id ultricies varius, sem ex venenatis erat, id posuere nunc quam quis metus' 37 | ), 38 | array( 39 | 'type' => 'quote', 40 | 'text' => 'God does not play dice', 41 | 'author' => 'Альберт Эйнштейн' 42 | ), 43 | array( 44 | 'type' => 'chart', 45 | 'chart_type' => 'bar', 46 | 'data' => array( 47 | array( 48 | array('apples', 'today', 'yesterday', 'd. bef. yesterday'), 49 | array('John', 4, 6, 7), 50 | array('Peter', 1, 3, 9), 51 | array('George', 4, 4, 3) 52 | ) 53 | ) 54 | ) 55 | ); 56 | 57 | $session = new RequestSigningSession($consumerKey, $consumerSecret); 58 | $request = new InfogramRequest($session, 'POST', 'infographics/', array('content' => $content, 'theme_id' => 215), $baseUrl); 59 | 60 | $response = $request->execute(); 61 | 62 | if (! $response) { 63 | die("Could not connect to the server\n"); 64 | } 65 | 66 | if (!$response->isOK()) { 67 | die('Could not execute request: ' . $response->getBody() . "\n"); 68 | } 69 | 70 | $id = $response->getHeader('X-Infogram-Id'); 71 | 72 | die('Infographic created, id: ' . $id . "\n"); 73 | 74 | -------------------------------------------------------------------------------- /examples/list-infographics.php: -------------------------------------------------------------------------------- 1 | -s -u \n" . 17 | "php list-infographics.php --key= --secret= --user=\n"); 18 | } 19 | 20 | $consumerKey = array_key_exists('k', $options) ? $options['k'] : $options['key']; 21 | $consumerSecret = array_key_exists('s', $options) ? $options['s'] : $options['secret']; 22 | $userName = array_key_exists('u', $options) ? $options['u'] : $options['user']; 23 | 24 | $baseUrl = null; 25 | if (array_key_exists('b', $options)) { 26 | $baseUrl = $options['b']; 27 | } 28 | else if (array_key_exists('base-url', $options)) { 29 | $baseUrl = $options['base-url']; 30 | } 31 | 32 | $session = new RequestSigningSession($consumerKey, $consumerSecret); 33 | $request = new InfogramRequest($session, 'GET', 'users/' . $userName . '/infographics', null, $baseUrl); 34 | 35 | $response = $request->execute(); 36 | 37 | if (! $response) { 38 | die("Cannot connect to the server\n"); 39 | } 40 | 41 | if (!$response->isOK()) { 42 | die("Could not execute request\n"); 43 | } 44 | 45 | 46 | $infographics = $response->getBody(); 47 | if (empty($infographics)) { 48 | die("There are no public infographics for this user\n"); 49 | } 50 | 51 | echo "ID" . str_repeat(' ', 36) . "Title\n"; 52 | foreach ($infographics as $infographic) { 53 | $id = $infographic->id; 54 | $title = $infographic->title; 55 | echo "$id" . str_repeat(' ', max(array(0, 38 - strlen($id)))) . "$title\n"; 56 | } 57 | -------------------------------------------------------------------------------- /examples/list-themes.php: -------------------------------------------------------------------------------- 1 | -s \nphp list-themes.php --key= --secret="); 14 | } 15 | 16 | $consumerKey = array_key_exists('k', $options) ? $options['k'] : $options['key']; 17 | $consumerSecret = array_key_exists('s', $options) ? $options['s'] : $options['secret']; 18 | 19 | $baseUrl = null; 20 | if (array_key_exists('b', $options)) { 21 | $baseUrl = $options['b']; 22 | } 23 | else if (array_key_exists('base-url', $options)) { 24 | $baseUrl = $options['base-url']; 25 | } 26 | 27 | $session = new RequestSigningSession($consumerKey, $consumerSecret); 28 | $request = new InfogramRequest($session, 'GET', 'themes', null, $baseUrl); 29 | 30 | $response = $request->execute(); 31 | 32 | if (! $response) { 33 | die("Could not connect to the server\n"); 34 | } 35 | 36 | if (!$response->isOK()) { 37 | die("Could not execute request\n"); 38 | } 39 | 40 | 41 | $themes = $response->getBody(); 42 | if (empty($themes)) { 43 | die("There are no themes available\n"); 44 | } 45 | 46 | echo "ID\t\tTitle\n"; 47 | foreach ($themes as $theme) { 48 | $id = $theme->id; 49 | $title = $theme->title; 50 | echo "$id\t\t$title\n"; 51 | } 52 | -------------------------------------------------------------------------------- /src/Infogram/BaseString.php: -------------------------------------------------------------------------------- 1 | getUrl(); 12 | $params = $request->getParameters(); 13 | $method = $request->getMethod(); 14 | 15 | $httpResponse = Requests::request($url, array(), $params, $method); 16 | 17 | if (!$httpResponse) { 18 | return null; 19 | } 20 | 21 | return new SimpleResponse($httpResponse->body, $httpResponse->headers, $httpResponse->status_code); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Infogram/InfogramRequest.php: -------------------------------------------------------------------------------- 1 | session = $session; 25 | $this->version = isset($version) ? $version : self::VERSION; 26 | $this->transport = isset($transport) ? $transport : new HttpTransport(); 27 | } 28 | 29 | public function execute() 30 | { 31 | $this->session->passThrough($this); 32 | $rawResponse = $this->transport->send($this); 33 | if ($rawResponse == null) { 34 | return null; 35 | } 36 | return new InfogramResponse($rawResponse); 37 | } 38 | 39 | public function getVersion() 40 | { 41 | return $this->version; 42 | } 43 | 44 | private static function convertCompoundParametersToStringIfNeeded($params) 45 | { 46 | $arr = array(); 47 | foreach ($params as $name => $value) { 48 | if (is_scalar($value)) { 49 | $arr[$name] = $value; 50 | } 51 | else { 52 | if (is_array($value) || is_object($value)) { 53 | $arr[$name] = json_encode($value); 54 | } 55 | else { 56 | throw new \ErrorException('Array contains a non-serializable value with name "' . $name . '"'); 57 | } 58 | } 59 | } 60 | return $arr; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Infogram/InfogramResponse.php: -------------------------------------------------------------------------------- 1 | getStatus(); 12 | $convertToJson = false; 13 | $ok = self::statusIsOK($status); 14 | if ($ok) { 15 | $contentType = $parent->getHeader('Content-Type'); 16 | if (!empty($contentType)) { 17 | $convertToJson = stristr($contentType, '/json') !== false; 18 | } 19 | } 20 | $bodyString = $parent->getBody(); 21 | parent::__construct($convertToJson ? json_decode($bodyString) : $bodyString, $parent->getHeaders(), $status); 22 | $this->ok = $ok; 23 | } 24 | 25 | function isOK() 26 | { 27 | return $this->ok; 28 | } 29 | 30 | private static function statusIsOK($status) 31 | { 32 | return $status >= 200 && $status < 300; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Infogram/InfogramSession.php: -------------------------------------------------------------------------------- 1 | consumerKey = $consumerKey; 15 | $this->consumerSecret = $consumerSecret; 16 | } 17 | 18 | public function passThrough(Request $request) 19 | { 20 | $params = $request->getParameters(); 21 | if (array_key_exists('api_key', $params)) { 22 | throw new \ErrorException('Request contains parameter with a reserved name: \'api_key\''); 23 | } 24 | if (array_key_exists('api_sig', $params)) { 25 | throw new \ErrorException('Request contains parameter with a reserved name: \'api_sig\''); 26 | } 27 | $params['api_key'] = $this->consumerKey; 28 | $baseString = BaseString::compute($request->getMethod(), $request->getUrl(), $params); 29 | $signature = base64_encode(hash_hmac('sha1', $baseString, rawurlencode($this->consumerSecret), true)); 30 | $request->setParameter('api_key', $this->consumerKey); 31 | $request->setParameter('api_sig', $signature); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Infogram/Response.php: -------------------------------------------------------------------------------- 1 | method = $method; 17 | $this->url = $url; 18 | $this->parameters = $params; 19 | } 20 | 21 | public function getMethod() 22 | { 23 | return $this->method; 24 | } 25 | 26 | public function getUrl() 27 | { 28 | return $this->url; 29 | } 30 | 31 | public function getParameters() 32 | { 33 | return $this->parameters; 34 | } 35 | 36 | public function setParameter($name, $value) { 37 | $this->parameters[$name] = $value; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Infogram/SimpleResponse.php: -------------------------------------------------------------------------------- 1 | body = $body; 11 | $this->headers = self::convertKeyNamesToLowerCase($headers); 12 | $this->status = $status; 13 | } 14 | 15 | public function getStatus() 16 | { 17 | return $this->status; 18 | } 19 | 20 | public function getBody() 21 | { 22 | return $this->body; 23 | } 24 | 25 | public function getHeaders() 26 | { 27 | return $this->headers; 28 | } 29 | 30 | public function getHeader($name) 31 | { 32 | $key = strtolower($name); 33 | if (array_key_exists($key, $this->headers)) { 34 | return $this->headers[$key]; 35 | } 36 | return null; 37 | } 38 | 39 | public static function convertKeyNamesToLowerCase(&$arr) 40 | { 41 | $ret = array(); 42 | foreach ($arr as $key => $value) { 43 | $ret[strtolower($key)] = $value; 44 | } 45 | return $ret; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Infogram/Transport.php: -------------------------------------------------------------------------------- 1 | 'apple', 13 | 'vegetable' => 'cucumber', 14 | 'salad' => 'green')); 15 | $this->assertEquals("POST&http%3A%2F%2Finfogram.local.com%3A1337%2Fservice%2Fgroceries&fruit%3Dapple%26salad%3Dgreen%26vegetable%3Dcucumber", $baseString); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Infogram/InfogramRequestTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('send')->andReturn(new SimpleResponse($templates, array('Content-Type' => 'application/json'), 200)); 16 | 17 | $session = Mockery::mock('Infogram\InfogramSession'); 18 | $session->shouldReceive('passThrough'); //noop 19 | 20 | $request = new InfogramRequest($session, 'GET', 'themes', null, null, null, $transport); 21 | $response = $request->execute(); 22 | $this->assertNotNull($response); 23 | $this->assertTrue($response->isOK()); 24 | 25 | $body = $response->getBody(); 26 | $this->assertTrue(is_array($body)); 27 | $this->assertEquals(3, count($body)); 28 | $this->assertEquals(1, $body[0]->id); 29 | $this->assertEquals('The Simpsons', $body[0]->title); 30 | $this->assertEquals(2, $body[1]->id); 31 | $this->assertEquals('The Jetsons', $body[1]->title); 32 | $this->assertEquals(3, $body[2]->id); 33 | $this->assertEquals('The Flintstones', $body[2]->title); 34 | } 35 | 36 | public function testExecute_shouldSerializeNonScalarsToJSON() 37 | { 38 | $transport = Mockery::mock('Infogram\Transport'); 39 | $session = Mockery::mock('Infogram\InfogramSession'); 40 | 41 | $parameters = array( 42 | 'scalarInt' => 123, 43 | 'scalarString' => 'foo', 44 | 'compoundArray' => array(4, 'five') 45 | ); 46 | 47 | $request = new InfogramRequest($session, 'POST', 'something', $parameters, null, null, $transport); 48 | 49 | $requestParameters = $request->getParameters(); 50 | 51 | $this->assertTrue(is_array($requestParameters)); 52 | $this->assertArrayHasKey('scalarInt', $requestParameters); 53 | $this->assertTrue($requestParameters['scalarInt'] === 123); 54 | $this->assertArrayHasKey('scalarString', $requestParameters); 55 | $this->assertTrue($requestParameters['scalarString'] === 'foo'); 56 | $this->assertArrayHasKey('compoundArray', $requestParameters); 57 | $this->assertTrue(is_string($requestParameters['compoundArray'])); 58 | $arr = json_decode($requestParameters['compoundArray']); 59 | $this->assertTrue($arr[0] === 4); 60 | $this->assertTrue($arr[1] === 'five'); 61 | } 62 | 63 | public function testExecute_transportReturnsNull_returnNull() 64 | { 65 | $transport = Mockery::mock('Infogram\Transport'); 66 | $transport->shouldReceive('send')->andReturn(null); 67 | 68 | $session = Mockery::mock('Infogram\InfogramSession'); 69 | $session->shouldReceive('passThrough'); 70 | 71 | $request = new InfogramRequest($session, 'GET', 'something', array(), null, null, $transport); 72 | 73 | $response = $request->execute(); 74 | 75 | $this->assertNull($response); 76 | } 77 | 78 | public function tearDown() 79 | { 80 | Mockery::close(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/Infogram/RequestSigningSessionTest.php: -------------------------------------------------------------------------------- 1 | 2, 'oranges' => 'many')); 12 | $session->passThrough($request); 13 | $params = $request->getParameters(); 14 | 15 | $this->assertArrayHasKey('api_key', $params); 16 | $this->assertEquals('john', $params['api_key']); 17 | 18 | $this->assertArrayHasKey('api_sig', $params); 19 | $this->assertEquals('x38tTpTI9SN0T2XRWZ/S0y0SwDQ=', $params['api_sig']); 20 | } 21 | 22 | /** 23 | * @expectedException ErrorException 24 | */ 25 | public function testPassThrough_containsKeyParameter_shouldThrowException() 26 | { 27 | $session = new RequestSigningSession('john', 'passw0rd'); 28 | $request = new SimpleRequest('GET', 'http://somewhere.vvz/foo/bar', array('one' => 1, 'two' => 2, 'api_key' => 'value')); 29 | $session->passThrough($request); 30 | } 31 | 32 | /** 33 | * @expectedException ErrorException 34 | */ 35 | public function testPassThrough_containsSignatureParameter_shouldThrowException() 36 | { 37 | $session = new RequestSigningSession('john', 'passw0rd'); 38 | $request = new SimpleRequest('GET', 'http://somewhere.vvz/foo/bar', array('one' => 1, 'two' => 2, 'api_sig' => 'asdfqwer')); 39 | $session->passThrough($request); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Infogram/SimpleResponseTest.php: -------------------------------------------------------------------------------- 1 | 'application/json')); 10 | 11 | $this->assertEquals('application/json', $response->getHeader('Content-Type')); 12 | $this->assertEquals('application/json', $response->getHeader('content-type')); 13 | 14 | } 15 | 16 | public function testGetHeader_nonExistent_shouldReturnNull() 17 | { 18 | $response = new SimpleResponse('blah', array()); 19 | $this->assertEquals(null, $response->getHeader('Content-Type')); 20 | } 21 | } 22 | --------------------------------------------------------------------------------