├── .gitignore ├── LICENSE ├── QuickChart.php ├── README.md ├── composer.json └── examples ├── api_key_simple.php ├── broken_config.php ├── short_url.php ├── simple.php ├── to_file.php └── with_function.php /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | 3 | ### Vim ### 4 | [._]*.s[a-w][a-z] 5 | [._]s[a-w][a-z] 6 | *.un~ 7 | Session.vim 8 | .netrwhist 9 | *~ 10 | 11 | 12 | ### OSX ### 13 | .DS_Store 14 | .AppleDouble 15 | .LSOverride 16 | 17 | # Icon must end with two \r 18 | Icon 19 | 20 | 21 | # Thumbnails 22 | ._* 23 | 24 | # Files that might appear in the root of a volume 25 | .DocumentRevisions-V100 26 | .fseventsd 27 | .Spotlight-V100 28 | .TemporaryItems 29 | .Trashes 30 | .VolumeIcon.icns 31 | 32 | # Directories potentially created on remote AFP share 33 | .AppleDB 34 | .AppleDesktop 35 | Network Trash Folder 36 | Temporary Items 37 | .apdisk 38 | 39 | 40 | ### Python ### 41 | # Byte-compiled / optimized / DLL files 42 | __pycache__/ 43 | *.py[cod] 44 | *$py.class 45 | 46 | # C extensions 47 | *.so 48 | 49 | # Distribution / packaging 50 | .Python 51 | env/ 52 | build/ 53 | develop-eggs/ 54 | dist/ 55 | downloads/ 56 | eggs/ 57 | .eggs/ 58 | lib/ 59 | lib64/ 60 | parts/ 61 | sdist/ 62 | var/ 63 | *.egg-info/ 64 | .installed.cfg 65 | *.egg 66 | 67 | # PyInstaller 68 | # Usually these files are written by a python script from a template 69 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 70 | *.manifest 71 | *.spec 72 | 73 | # Installer logs 74 | pip-log.txt 75 | pip-delete-this-directory.txt 76 | 77 | # Unit test / coverage reports 78 | htmlcov/ 79 | .tox/ 80 | .coverage 81 | .coverage.* 82 | .cache 83 | nosetests.xml 84 | coverage.xml 85 | *,cover 86 | 87 | # Translations 88 | *.mo 89 | *.pot 90 | 91 | # Django stuff: 92 | *.log 93 | 94 | # Sphinx documentation 95 | docs/_build/ 96 | 97 | # PyBuilder 98 | target/ 99 | 100 | 101 | ### Node ### 102 | # Logs 103 | logs 104 | *.log 105 | npm-debug.log* 106 | 107 | # Runtime data 108 | pids 109 | *.pid 110 | *.seed 111 | 112 | # Directory for instrumented libs generated by jscoverage/JSCover 113 | lib-cov 114 | 115 | # Coverage directory used by tools like istanbul 116 | coverage 117 | 118 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 119 | .grunt 120 | 121 | # node-waf configuration 122 | .lock-wscript 123 | 124 | # Compiled binary addons (http://nodejs.org/api/addons.html) 125 | build/Release 126 | 127 | # Dependency directory 128 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 129 | node_modules 130 | 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 QuickChart.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QuickChart.php: -------------------------------------------------------------------------------- 1 | protocol = isset($options['protocol']) ? $options['protocol'] : 'https'; 22 | $this->host = isset($options['host']) ? $options['host'] : 'quickchart.io'; 23 | $this->port = isset($options['port']) ? $options['port'] : 443; 24 | $this->path = isset($options['path']) ? $options['path'] : false; 25 | $this->width = isset($options['width']) ? $options['width'] : 500; 26 | $this->height = isset($options['height']) ? $options['height'] : 300; 27 | $this->devicePixelRatio = isset($options['devicePixelRatio']) ? $options['devicePixelRatio'] : 1.0; 28 | $this->format = isset($options['format']) ? $options['format'] : 'png'; 29 | $this->backgroundColor = isset($options['backgroundColor']) ? $options['backgroundColor'] : 'transparent'; 30 | $this->apiKey = isset($options['apiKey']) ? $options['apiKey'] : null; 31 | $this->version = isset($options['version']) ? $options['version'] : null; 32 | } 33 | 34 | function setConfig($chartjsConfig) { 35 | $this->config = $chartjsConfig; 36 | } 37 | 38 | function setWidth($width) { 39 | $this->width = $width; 40 | } 41 | 42 | function setHeight($height) { 43 | $this->height = $height; 44 | } 45 | 46 | function setDevicePixelRatio($devicePixelRatio) { 47 | $this->devicePixelRatio = $devicePixelRatio; 48 | } 49 | 50 | function setFormat($format) { 51 | $this->format = $format; 52 | } 53 | 54 | function setBackgroundColor($backgroundColor) { 55 | $this->backgroundColor = $backgroundColor; 56 | } 57 | 58 | function setApiKey($apiKey) { 59 | $this->apiKey = $apiKey; 60 | } 61 | 62 | function setVersion($version) { 63 | $this->version = $version; 64 | } 65 | 66 | function getConfigStr() { 67 | if (is_array($this->config)) { 68 | return json_encode($this->config); 69 | } 70 | return $this->config; 71 | } 72 | 73 | function getUrl() { 74 | $configStr = urlencode($this->getConfigStr()); 75 | $width = $this->width; 76 | $height = $this->height; 77 | $devicePixelRatio = number_format($this->devicePixelRatio, 1); 78 | $format = $this->format; 79 | $backgroundColor = $this->backgroundColor; 80 | 81 | $url = sprintf($this->getRootEndpoint() . '/chart?ref=qc-php&c=%s&w=%d&h=%d&devicePixelRatio=%f&format=%s&bkg=%s', $configStr, $width, $height, $devicePixelRatio, $format, $backgroundColor); 82 | 83 | if ($this->apiKey) { 84 | $url .= '&key=' . $this->apiKey; 85 | } 86 | 87 | if ($this->version) { 88 | $url .= '&v=' . $this->version; 89 | } 90 | 91 | return $url; 92 | } 93 | 94 | function getShortUrl() { 95 | if ($this->host != 'quickchart.io') { 96 | throw new Exception('Short URLs must use quickchart.io host'); 97 | } 98 | $ch = curl_init($this->getRootEndpoint() . '/chart/create'); 99 | $postData = array( 100 | 'backgroundColor' => $this->backgroundColor, 101 | 'width' => $this->width, 102 | 'height' => $this->height, 103 | 'devicePixelRatio' => number_format($this->devicePixelRatio, 1), 104 | 'format' => $this->format, 105 | 'chart' => $this->getConfigStr(), 106 | ); 107 | if ($this->apiKey) { 108 | $postData['key'] = $this->apiKey; 109 | } 110 | if ($this->version) { 111 | $postData['version'] = $this->version; 112 | } 113 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData)); 114 | curl_setopt($ch, CURLOPT_HTTPHEADER, array( 115 | 'Content-Type:application/json', 116 | "User-Agent:" . QuickChart::USER_AGENT, 117 | )); 118 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 119 | $result = curl_exec($ch); 120 | 121 | if ($result === false) { 122 | throw new Exception(curl_error($ch), curl_errno($ch)); 123 | } 124 | 125 | curl_close($ch); 126 | // Note: do not dereference json_decode directly for 5.3 compatibility. 127 | $ret = json_decode($result, true); 128 | return $ret['url']; 129 | } 130 | 131 | function toBinary() { 132 | $ch = curl_init($this->getRootEndpoint() . '/chart'); 133 | $postData = array( 134 | 'backgroundColor' => $this->backgroundColor, 135 | 'devicePixelRatio' => $this->devicePixelRatio, 136 | 'width' => $this->width, 137 | 'height' => $this->height, 138 | 'format' => $this->format, 139 | 'chart' => $this->getConfigStr(), 140 | ); 141 | if ($this->apiKey) { 142 | $postData['key'] = $this->apiKey; 143 | } 144 | if ($this->version) { 145 | $postData['version'] = $this->version; 146 | } 147 | 148 | $responseHeaders = []; 149 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData)); 150 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json')); 151 | curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$responseHeaders) { 152 | $len = strlen($header); 153 | $header = explode(':', $header, 2); 154 | if (count($header) < 2) { // ignore invalid headers 155 | return $len; 156 | } 157 | $responseHeaders[strtolower(trim($header[0]))][] = trim($header[1]); 158 | return $len; 159 | }); 160 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 161 | $result = curl_exec($ch); 162 | $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 163 | 164 | if ($result === false) { 165 | $error = curl_error($ch); 166 | throw new Exception("Curl error: $error"); 167 | } 168 | 169 | curl_close($ch); 170 | 171 | if ($httpStatusCode >= 200 && $httpStatusCode < 300) { 172 | return $result; 173 | } 174 | 175 | $errorHeader = isset($responseHeaders['x-quickchart-error'][0]) ? $responseHeaders['x-quickchart-error'][0] : null; 176 | if ($errorHeader) { 177 | throw new Exception("QuickChart API returned error with status code $httpStatusCode: $errorHeader"); 178 | } 179 | 180 | throw new Exception("QuickChart API returned error with status code $httpStatusCode"); 181 | } 182 | 183 | function toFile($path) { 184 | $data = $this->toBinary(); 185 | file_put_contents($path, $data); 186 | } 187 | 188 | protected function getRootEndpoint() { 189 | return $this->protocol . '://' . $this->host . ( $this->port ? ':' . $this->port : '' ) . ( $this->path ? $this->path : '' ) ; 190 | } 191 | } 192 | 193 | 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickchart-php 2 | [![Packagist VERSION](http://img.shields.io/packagist/v/ianw/quickchart.svg?style=flat)](https://packagist.org/packages/ianw/quickchart) 3 | 4 | A PHP client for the [quickchart.io](https://quickchart.io/) chart image API. 5 | 6 | # Installation 7 | 8 | Use the `QuickChart.php` library in this project, or [install from packagist](https://packagist.org/packages/ianw/quickchart). 9 | 10 | ``` 11 | composer require ianw/quickchart 12 | ``` 13 | 14 | # Usage 15 | 16 | This library provides a `QuickChart` class. Import and instantiate it. Then set properties on it and specify a [Chart.js](https://chartjs.org) config: 17 | 18 | ```php 19 | $chart = new QuickChart(array( 20 | 'width' => 500, 21 | 'height' => 300 22 | )); 23 | 24 | $chart->setConfig('{ 25 | type: "bar", 26 | data: { 27 | labels: ["Hello world", "Test"], 28 | datasets: [{ 29 | label: "Foo", 30 | data: [1, 2] 31 | }] 32 | } 33 | }'); 34 | ``` 35 | 36 | Use `getUrl()` on your QuickChart object to get the encoded URL that renders your chart: 37 | 38 | ```php 39 | echo $chart->getUrl(); 40 | // https://quickchart.io/chart?c=%7B%22type%22%3A%22bar%22%2C%22data%22%3A%7B%22labels%22%3A%5B%22Hello+world%22%2C%22Test%22%5D%2C%22datasets%22%3A%5B%7B%22label%22%3A%22Foo%22%2C%22data%22%3A%5B1%2C2%5D%7D%5D%7D%7D&w=500&h=300 41 | ``` 42 | 43 | If you have a long or complicated chart, use `getShortUrl()` to get a fixed-length URL using the quickchart.io web service (note that these URLs only persist for a short time unless you have a subscription): 44 | 45 | ```php 46 | echo $chart->getShortUrl(); 47 | // https://quickchart.io/chart/render/f-a1d3e804-dfea-442c-88b0-9801b9808401 48 | ``` 49 | 50 | The URLs will render an image of a chart: 51 | 52 | 53 | 54 | ## Creating the chart object 55 | 56 | The `QuickChart` class constructor accepts an array containing the following keys. All are optional and can be set after object creation: 57 | 58 | ### config: array or string 59 | The actual Chart.js chart configuration. 60 | 61 | ### width: int 62 | Width of the chart image in pixels. Defaults to 500 63 | 64 | ### height: int 65 | Height of the chart image in pixels. Defaults to 300 66 | 67 | ### format: string 68 | Format of the chart. Defaults to png. 69 | 70 | ### backgroundColor: string 71 | The background color of the chart. Any valid HTML color works. Defaults to #ffffff (white). Also takes rgb, rgba, and hsl values. 72 | 73 | ### devicePixelRatio: float 74 | The device pixel ratio of the chart. This will multiply the number of pixels by the value. This is usually used for retina displays. Defaults to 1.0. 75 | 76 | ### version: string 77 | The Chart.js version to use. See [documentation](https://quickchart.io/documentation/#parameters) for supported versions. 78 | 79 | ### apiKey: string 80 | Your QuickChart API key, if you have one. 81 | 82 | ## Setting properties 83 | 84 | Each option above has an associated function call that you can invoke on your `QuickChart` object: 85 | 86 | - `setConfig($config)` 87 | - `setWidth($width)` 88 | - `setHeight($height)` 89 | - `setFormat($format)` 90 | - `setBackgroundColor($backgroundColor)` 91 | - `setDevicePixelRatio($devicePixelRatio)` 92 | - `setVersion($version)` 93 | - `setApiKey($apiKey)` 94 | 95 | ## Getting URLs 96 | 97 | There are two ways to get a URL for your chart object. 98 | 99 | ### getUrl: string 100 | 101 | Returns a URL that will display the chart image when loaded. 102 | 103 | ### getShortUrl: string 104 | 105 | Uses the quickchart.io web service to create a fixed-length chart URL that displays the chart image. Returns a URL such as `https://quickchart.io/chart/render/f-a1d3e804-dfea-442c-88b0-9801b9808401`. 106 | 107 | Note that short URLs expire after a few days for users of the free service. You can [subscribe](https://quickchart.io/pricing/) to keep them around longer. 108 | 109 | ## Other outputs 110 | 111 | ### toBinary: binary string 112 | 113 | Returns a binary string representing the chart image 114 | 115 | ### toFile($path: string) 116 | 117 | Write the image to a file 118 | 119 | For example: 120 | ```php 121 | $chart->toFile('/tmp/myfile.png') 122 | ``` 123 | 124 | ## More examples 125 | 126 | Checkout the `examples` directory to see other usage. 127 | 128 | ## Troubleshooting 129 | 130 | **PHP5 users**: This package requires curl and json modules. 131 | 132 | **sslv3 handshake alert failure**: You are using an outdated version of `curl`. Please upgrade curl on your machine. 133 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ianw/quickchart", 3 | "description": "QuickChart chart API", 4 | "keywords": [ 5 | "chart", "charts", "image", "bar-chart", "line-chart", "chart-api" 6 | ], 7 | "homepage": "http://github.com/typpo/quickchart-php", 8 | "license": "MIT", 9 | "authors": [{ 10 | "name": "Ian Webster", 11 | "homepage": "https://quickchart.io/" 12 | }], 13 | "require": { 14 | "php": ">=5.3.0" 15 | }, 16 | "archive": { 17 | "exclude": ["*", "!QuickChart.php", "!README.md", "!composer.json"] 18 | }, 19 | "autoload": { 20 | "psr-4": {"": ""} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/api_key_simple.php: -------------------------------------------------------------------------------- 1 | setConfig("{ 6 | type: 'bar', 7 | data: { 8 | labels: ['Q1', 'Q2', 'Q3', 'Q4'], 9 | datasets: [{ 10 | label: 'Users', 11 | data: [50, 60, 70, 180] 12 | }] 13 | } 14 | }"); 15 | $qc->setApiKey('abc123'); 16 | 17 | echo $qc->getUrl(); 18 | // Prints: 19 | // https://quickchart.io/chart?c=%7B%0A++++type%3A+%27bar%27%2C%0A++++data%3A+%7B%0A++++++labels%3A+%5B%27Q1%27%2C+%27Q2%27%2C+%27Q3%27%2C+%27Q4%27%5D%2C%0A++++++datasets%3A+%5B%7B%0A++++++++label%3A+%27Users%27%2C%0A++++++++data%3A+%5B50%2C+60%2C+70%2C+180%5D%0A++++++%7D%5D%0A++++%7D%0A++%7D&w=500&h=300&devicePixelRatio=1.000000&format=png&bkg=transparent&key=abc123 20 | ?> 21 | -------------------------------------------------------------------------------- /examples/broken_config.php: -------------------------------------------------------------------------------- 1 | setConfig("{ 6 | type: 'unknownfoobar', 7 | data: { 8 | labels: ['Q1', 'Q2', 'Q3', 'Q4'], 9 | datasets: [{ 10 | label: 'Users', 11 | data: [50, 60, 70, 180] 12 | }] 13 | } 14 | }"); 15 | 16 | echo $qc->toFile('/tmp/chart.png'); 17 | ?> 18 | -------------------------------------------------------------------------------- /examples/short_url.php: -------------------------------------------------------------------------------- 1 | setConfig("{ 6 | type: 'bar', 7 | data: { 8 | labels: ['Q1', 'Q2', 'Q3', 'Q4'], 9 | datasets: [{ 10 | label: 'Users', 11 | data: [50, 60, 70, 180] 12 | }] 13 | } 14 | }"); 15 | 16 | echo $qc->getShortUrl(); 17 | // Prints: 18 | // https://quickchart.io/chart/render/zf-16815ac3-850f-4f5f-a79c-f6386c688f48 19 | ?> 20 | 21 | -------------------------------------------------------------------------------- /examples/simple.php: -------------------------------------------------------------------------------- 1 | setConfig("{ 6 | type: 'bar', 7 | data: { 8 | labels: ['Q1', 'Q2', 'Q3', 'Q4'], 9 | datasets: [{ 10 | label: 'Users', 11 | data: [50, 60, 70, 180] 12 | }] 13 | } 14 | }"); 15 | 16 | echo $qc->getUrl(); 17 | // Prints: 18 | // https://quickchart.io/chart?c=%7B%0A++++type%3A+%27bar%27%2C%0A++++data%3A+%7B%0A++++++labels%3A+%5B%27Q1%27%2C+%27Q2%27%2C+%27Q3%27%2C+%27Q4%27%5D%2C%0A++++++datasets%3A+%5B%7B%0A++++++++label%3A+%27Users%27%2C%0A++++++++data%3A+%5B50%2C+60%2C+70%2C+180%5D%0A++++++%7D%5D%0A++++%7D%0A++%7D&w=500&h=300&devicePixelRatio=1.0&format=png&bkg=transparent 19 | ?> 20 | -------------------------------------------------------------------------------- /examples/to_file.php: -------------------------------------------------------------------------------- 1 | setConfig("{ 6 | type: 'bar', 7 | data: { 8 | labels: ['Q1', 'Q2', 'Q3', 'Q4'], 9 | datasets: [{ 10 | label: 'Users', 11 | data: [50, 60, 70, 180] 12 | }] 13 | } 14 | }"); 15 | 16 | echo $qc->toFile('/tmp/chart.png'); 17 | ?> 18 | -------------------------------------------------------------------------------- /examples/with_function.php: -------------------------------------------------------------------------------- 1 | { 23 | return val + 'k'; 24 | } 25 | } 26 | }] 27 | } 28 | } 29 | } 30 | EOD; 31 | 32 | $qc->setConfig($config); 33 | 34 | echo $qc->getUrl(); 35 | // Prints: https://quickchart.io/chart?c=%7B%0A++++type%3A+%27bar%27%2C%0A++++data%3A+%7B%0A++++++labels%3A+%5B%27Q1%27%2C+%27Q2%27%2C+%27Q3%27%2C+%27Q4%27%5D%2C%0A++++++datasets%3A+%5B%7B%0A++++++++label%3A+%27Users%27%2C%0A++++++++data%3A+%5B50%2C+60%2C+70%2C+180%5D%0A++++++%7D%2C+%7B%0A++++++++label%3A+%27Revenue%27%2C%0A++++++++data%3A+%5B100%2C+200%2C+300%2C+400%5D%0A++++++%7D%5D%0A++++%7D%2C%0A++++options%3A+%7B%0A++++++scales%3A+%7B%0A++++++++yAxes%3A+%5B%7B%0A++++++++++ticks%3A+%7B%0A++++++++++++callback%3A+%28val%29+%3D%3E+%7B%0A++++++++++++++return+val+%2B+%27k%27%3B%0A++++++++++++%7D%0A++++++++++%7D%0A++++++++%7D%5D%0A++++++%7D%0A++++%7D%0A++%7D&w=500&h=300&devicePixelRatio=1.000000&format=png&bkg=transparent(base) 36 | ?> 37 | --------------------------------------------------------------------------------