├── .gitattributes ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Api │ ├── AbstractBaseApi.php │ └── AuthInterface.php ├── ImageClient.php └── Response │ └── Response.php └── tests └── ImageClientTest.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Define the line ending behavior of the different file extensions 2 | # Set default behaviour, in case users don't have core.autocrlf set. 3 | * text=auto 4 | * text eol=lf 5 | 6 | # Explicitly declare text files we want to always be normalized and converted 7 | # to native line endings on checkout. 8 | *.php text 9 | *.default text 10 | *.ctp text 11 | *.sql text 12 | *.md text 13 | *.po text 14 | *.js text 15 | *.css text 16 | *.ini text 17 | *.properties text 18 | *.txt text 19 | *.xml text 20 | *.svg text 21 | *.yml text 22 | .htaccess text 23 | 24 | # Declare files that will always have CRLF line endings on checkout. 25 | *.bat eol=crlf 26 | 27 | # Declare files that will always have LF line endings on checkout. 28 | *.pem eol=lf 29 | 30 | # Denote all files that are truly binary and should not be modified. 31 | *.png binary 32 | *.jpg binary 33 | *.gif binary 34 | *.ico binary 35 | *.mo binary 36 | *.pdf binary 37 | *.phar binary 38 | *.woff binary 39 | *.woff2 binary 40 | *.ttf binary 41 | *.otf binary 42 | *.eot binary 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.project 3 | /.buildpath 4 | /vendor 5 | composer.lock 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | - 7.0 6 | - 7.1 7 | - 7.2 8 | 9 | before_script: 10 | - composer self-update 11 | - composer install --prefer-source --no-interaction --dev 12 | 13 | script: phpunit 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at git@phpfanatic.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 PHPfanatic (nw) 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clarifai PHP Library 2 | [![Build Status](https://travis-ci.org/PHPfanatic/clarifai.svg?branch=master)](https://travis-ci.org/PHPfanatic/clarifai) 3 | [![GitHub version](https://badge.fury.io/gh/phpfanatic%2Fclarifai.png)](https://badge.fury.io/gh/phpfanatic%2Fclarifai) 4 | 5 | Clarifai API Library, PHP/Composer implementation of the clarifai api. ([clarifai](https://clarifai.com/)). 6 | PHPfanatic's PHP library brings you the power of clarifai's image recognition API wrapped in an easy to use PHP library that you can 7 | add to your own project easily with composer. 8 | 9 | Build smarter apps faster with Clarifai’s powerful visual recognition technology. 10 | 11 | ## Getting Started 12 | 13 | Add the package to your composer implementation. 14 | ``` 15 | composer require phpfanatic/clarifai 16 | ``` 17 | 18 | or 19 | 20 | Add the package manually by downloading the most recent stable version release from Github and include the src/ directory 21 | within your own project. 22 | 23 | * [zip](https://github.com/PHPfanatic/clarifai/archive/2.0.1.zip) 24 | 25 | * [tar](https://github.com/PHPfanatic/clarifai/archive/2.0.1.gz) 26 | 27 | ### Requirements 28 | 29 | * PHP - 5.6, 7.0 - May work with ealier version, untested at this time. 30 | * cURL - * 31 | * Clarifai API Key - [clarifai](https://developer.clarifai.com/pricing/) 32 | * PHPUnit - to run tests (optional). 33 | 34 | ### Example Usage 35 | 36 | ``` 37 | use PhpFanatic\clarifAI\ImageClient; 38 | 39 | $client = new ImageClient([API_KEY]); 40 | 41 | $client->AddImage('http://phpfanatic.com/projects/clarifai/dog.jpg'); 42 | $result = $client->Predict(); 43 | ``` 44 | 45 | ## Documentation 46 | 47 | [PHPfanatic - ClarifAI documentation](https://github.com/PHPfanatic/clarifai/wiki/) 48 | 49 | ## Change History 50 | * 2.0.1 - Patched clientversion variable. 51 | * 2.0.0 - Changed Clarifai authentication to use API Key method. 52 | * 1.2.3 - Added User-Agent Library header to outbound curl calls per Clarifai request. 53 | * 1.2.2 - Fixed malformed url bug when you paginate a get request. (credit: @mhdere) 54 | * 1.2.1 - Fixed urlencode bug (credit: keithslater), added additional Clarifai models. 55 | * 1.2.0 - Added model delete support, updated inputs to allow delete all. 56 | * 1.1.0 - Added multi language support. 57 | * 1.0.0 - Public API release. 58 | * 0.1.1 - Development alpha release. 59 | 60 | ## Look Ahead (what might be changed in the future.) 61 | 62 | * Removing cURL as a requirement. 63 | * Combining POST,DELETE,PATCH into a single method. 64 | 65 | ## Built With 66 | 67 | * [Composer](https://getcomposer.org/) - Dependency management 68 | * [PHPUnit](https://phpunit.de/) - Testing framework 69 | * [Packagist](https://packagist.org/) - Package repository 70 | * [Travis CI](https://travis-ci.org/) - Automated building 71 | 72 | ## Authors 73 | 74 | * **Nick White** - *Initial work* - [PHPfanatic](https://github.com/PHPfanatic) 75 | 76 | ## License 77 | 78 | This project is licensed under the MIT License. 79 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpfanatic/clarifai", 3 | "description": "clarifAI PHP Library", 4 | "type": "library", 5 | "license": "MIT", 6 | "homepage": "https://github.com/PHPfanatic/clarifai", 7 | "keywords": ["php","clarifai","machine learning","recognition"], 8 | "authors": [ 9 | { 10 | "name": "Nick White", 11 | "email": "git@phpfanatic.com" 12 | } 13 | ], 14 | "require": { 15 | "php": "^5.6|^7.0" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "5.5.*" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "PhpFanatic\\clarifAI\\": "src" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Api/AbstractBaseApi.php: -------------------------------------------------------------------------------- 1 | 8 | * @link https://github.com/PHPfanatic/clarifai 9 | * @version 2.0.1 10 | */ 11 | 12 | abstract class AbstractBaseApi implements AuthInterface 13 | { 14 | public $apikey = null; 15 | private $clientversion = '2.0.1'; 16 | private $endpoint = 'https://api.clarifai.com'; 17 | private $version = 'v2'; 18 | private $apiurl = null; 19 | 20 | /** 21 | * Initial ImageClient setup. 22 | * @param string $clientid 23 | * @param string $clientsecret 24 | */ 25 | public function __construct($apikey) { 26 | $this->SetApiKey($apikey); 27 | $this->SetApiUrl(); 28 | } 29 | 30 | /** 31 | * Set api key 32 | * {@inheritDoc} 33 | * @see \PhpFanatic\clarifAI\Api\AuthInterface::SetApiKey() 34 | */ 35 | public function SetApiKey($apikey) { 36 | $this->apikey = $apikey; 37 | } 38 | 39 | /** 40 | * Place holder for future setting of client version. 41 | * @param string $clientversion 42 | */ 43 | public function GetClientVersion() { 44 | return $this->clientversion; 45 | } 46 | 47 | /** 48 | * Returns the custom agent header. This was a ClarifAI request to better help them debug client inquiries. 49 | * @return string 50 | */ 51 | public function GetClientAgent() { 52 | return 'Clarifai PHP (https://github.com/PHPfanatic/clarifai);'.$this->GetClientVersion().';'.phpversion(); 53 | } 54 | 55 | /** 56 | * Sets a new endpoint url. Calling this method also updates the Api url. 57 | * @example $client->SetEndPoint('http://api.clarifai.com'); 58 | * @param string $endpoint 59 | */ 60 | public function SetEndPoint($endpoint) { 61 | $this->endpoint = $endpoint; 62 | $this->SetApiUrl(); 63 | } 64 | 65 | /** 66 | * Sets a new clarifAI version to work with. Calling this method also updates the Api url. 67 | * @example $client->SetVersion('v2'); 68 | * @param string $version 69 | */ 70 | public function SetVersion($version) { 71 | $this->version = $version; 72 | $this->SetApiUrl(); 73 | } 74 | 75 | /** 76 | * Sets the api url. 77 | */ 78 | private function SetApiUrl() { 79 | $this->apiurl = $this->endpoint . '/' . $this->version; 80 | } 81 | 82 | /** 83 | * Validate if a Api Key is set and if it is valid. 84 | * {@inheritDoc} 85 | * @see \PhpFanatic\clarifAI\Api\AuthInterface::IsTokenValid() 86 | * @return bool 87 | */ 88 | public function IsApiKeySet() { 89 | if(!isset($this->apikey) && is_null($this->apikey)) { 90 | return false; 91 | } 92 | 93 | return true; 94 | } 95 | 96 | 97 | /** 98 | * Send a POST request to clarifAI API. 99 | * @param string $data json inputs string. 100 | * @param string $service appended to the apiurl when making the API call. 101 | * @return string 102 | */ 103 | public function SendPost($data, $service) { 104 | $ch = curl_init(); 105 | 106 | $header = array(); 107 | $header[] = 'Content-type: application/json'; 108 | $header[] = 'Authorization: Key ' . $this->apikey; 109 | 110 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 111 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); 112 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 113 | curl_setopt($ch, CURLOPT_URL, $this->apiurl . '/' . $service); 114 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 115 | curl_setopt($ch, CURLOPT_USERAGENT, $this->GetClientAgent()); 116 | 117 | $result = curl_exec($ch); 118 | curl_close($ch); 119 | 120 | return $result; 121 | } 122 | 123 | /** 124 | * Send a Delete request to clarifAI API. 125 | * @param string $data json inputs string. 126 | * @param string $service appended to the apiurl when making the API call. 127 | * @return string 128 | */ 129 | public function SendDelete($data, $service) { 130 | $ch = curl_init(); 131 | 132 | $header = array(); 133 | $header[] = 'Content-type: application/json'; 134 | $header[] = 'Authorization: Key ' . $this->apikey; 135 | 136 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 137 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); 138 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 139 | curl_setopt($ch, CURLOPT_URL, $this->apiurl . '/' . $service); 140 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 141 | curl_setopt($ch, CURLOPT_USERAGENT, $this->GetClientAgent()); 142 | 143 | $result = curl_exec($ch); 144 | curl_close($ch); 145 | 146 | return $result; 147 | } 148 | 149 | /** 150 | * Send a PATCH request to clarifAI API. 151 | * @param string $data json inputs string. 152 | * @param string $service appended to the apiurl when making the API call. 153 | * @return string 154 | */ 155 | public function SendPatch($data, $service='inputs') { 156 | $ch = curl_init(); 157 | 158 | $header = array(); 159 | $header[] = 'Content-type: application/json'; 160 | $header[] = 'Authorization: Key ' . $this->apikey; 161 | 162 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 163 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); 164 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 165 | curl_setopt($ch, CURLOPT_URL, $this->apiurl . '/' . $service); 166 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 167 | curl_setopt($ch, CURLOPT_USERAGENT, $this->GetClientAgent()); 168 | 169 | $result = curl_exec($ch); 170 | curl_close($ch); 171 | 172 | return $result; 173 | } 174 | 175 | /** 176 | * Send a GET request to clarifAI API. 177 | * @param array $data 178 | * @param string $service 179 | * @return mixed 180 | */ 181 | public function SendGet($data=array(), $service='inputs') { 182 | $ch = curl_init(); 183 | 184 | $data = implode('/', array_filter($data)); 185 | 186 | $header = array(); 187 | $header[] = 'Content-type: application/json'; 188 | $header[] = 'Authorization: Key ' . $this->apikey; 189 | 190 | if($data === '') { 191 | $url = $this->apiurl . '/' . $service; 192 | } else { 193 | $url = $this->apiurl . '/' . $service . '/' . $data; 194 | } 195 | 196 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 197 | curl_setopt($ch, CURLOPT_URL, $url); 198 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 199 | curl_setopt($ch, CURLOPT_USERAGENT, $this->GetClientAgent()); 200 | 201 | $result = curl_exec($ch); 202 | curl_close($ch); 203 | 204 | return $result; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/Api/AuthInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * @link https://github.com/PHPfanatic/clarifai 7 | * @version 2.0.1 8 | */ 9 | 10 | interface AuthInterface { 11 | 12 | /** 13 | * Set API Key for authentication. 14 | */ 15 | public function SetApiKey($apikey); 16 | } 17 | -------------------------------------------------------------------------------- /src/ImageClient.php: -------------------------------------------------------------------------------- 1 | 6 | * @link https://github.com/PHPfanatic/clarifai 7 | * @version 2.0.1 8 | */ 9 | 10 | use PhpFanatic\clarifAI\Api\AbstractBaseApi; 11 | use PhpFanatic\clarifAI\Response\Response; 12 | 13 | class ImageClient extends AbstractBaseApi 14 | { 15 | public $data = array(); 16 | public $image; 17 | public $search; 18 | public $concept; 19 | public $paginate; 20 | 21 | 22 | private $models = [ 23 | 'General'=>'aaa03c23b3724a16a56b629203edc62c', 24 | 'Adult'=>'e9576d86d2004ed1a38ba0cf39ecb4b1', 25 | 'Weddings'=>'c386b7a870114f4a87477c0824499348', 26 | 'Travel'=>'eee28c313d69466f836ab83287a54ed9', 27 | 'Food'=>'bd367be194cf45149e75f01d59f77ba7', 28 | 'Color'=>'eeed0b6733a644cea07cf4c60f87ebb7', 29 | 'Apparel'=>'e0be3b9d6a454f0493ac3a30784001ff', 30 | 'Celebrity'=>'e466caa0619f444ab97497640cefc4dc', 31 | 'Face'=>'a403429f2ddf4b49b307e318f00e528b', 32 | 'Demographics'=>'c0c0ac362b03416da06ab3fa36fb58e3', 33 | 'Focus'=>'c2cf7cecd8a6427da375b9f35fcd2381', 34 | 'Logo'=>'c443119bf2ed4da98487520d01a0b1e3', 35 | 'Embedding'=>'bbb5f41425b8468d9b7a554ff10f8581' 36 | ]; 37 | 38 | private $languages = [ 39 | 'ar'=>'Arabic', 40 | 'bn'=>'Bengali', 41 | 'da'=>'Danish', 42 | 'de'=>'German', 43 | 'en'=>'English', 44 | 'es'=>'Spanish', 45 | 'fi'=>'Finnish', 46 | 'fr'=>'French', 47 | 'hi'=>'Hindi', 48 | 'hu'=>'Hungarian', 49 | 'it'=>'Italian', 50 | 'ja'=>'Japanese', 51 | 'ko'=>'Korean', 52 | 'nl'=>'Dutch', 53 | 'no'=>'Norwegian', 54 | 'pa'=>'Punjabi', 55 | 'pl'=>'Polish', 56 | 'pt'=>'Portuguese', 57 | 'ru'=>'Russian', 58 | 'sv'=>'Swedish', 59 | 'tr'=>'Turkish', 60 | 'zh'=>'Chinese Simplified', 61 | 'zh-TW'=>'Chinese Traditional' 62 | ]; 63 | 64 | public function __construct($apikey) { 65 | parent::__construct($apikey); 66 | } 67 | 68 | /** 69 | * Create a model. 70 | * @param string $model 71 | * @param string $model_id 72 | * @return string Json response from ClarifAI. 73 | */ 74 | public function ModelAdd($model, $model_id) { 75 | $data = array('model'=>array('name'=>$model, 'id'=>$model_id)); 76 | $json = json_encode($data); 77 | 78 | if(!$this->IsApiKeySet()) { 79 | throw new \ErrorException('No API Key Set'); 80 | } 81 | 82 | $service='models'; 83 | $result = $this->SendPost($json, $service); 84 | 85 | return (Response::GetJson($result)); 86 | } 87 | 88 | /** 89 | * Update a model with a concept 90 | * @param string $id Model ID to be updated. 91 | * @param array $concepts Array of concepts to be applied. 92 | * @param string $action action to apply merge|remove. 93 | * @throws \ErrorException 94 | * @return string Json response from ClarifAI. 95 | */ 96 | public function ModelUpdate($id, $concept, $action='merge') { 97 | $build = array('id'=>$id, 'output_info'=>array('data'=>array('concepts'=>$concept))); 98 | $data['models'][] = $build; 99 | $data['action']=$action; 100 | 101 | if(!$this->IsApiKeySet()) { 102 | throw new \ErrorException('No API Key Set'); 103 | } 104 | 105 | $service = 'models'; 106 | $json = json_encode($data); 107 | 108 | $result = $this->SendPatch($json, $service); 109 | 110 | return (Response::GetJson($result)); 111 | } 112 | 113 | /** 114 | * Delete all models, delete specific model id or delete a specific version of a model. 115 | * As of 3/20/2017 'delete_all' must be passed when an ID is not specified. This is not reflected 116 | * in the ClarifAI documentation however it is needed. 117 | * 118 | * @param string $id 119 | * @param string $version 120 | * @throws \ErrorException 121 | * @return string Json response from ClarifAI. 122 | */ 123 | public function ModelDelete($id=null, $version=null) { 124 | 125 | if(!$this->IsApiKeySet()) { 126 | throw new \ErrorException('No API Key Set'); 127 | } 128 | 129 | $service="models"; 130 | 131 | if($id != null) { 132 | $service .= '/' . urlencode($id); 133 | } 134 | 135 | if($id != null && $version != null) { 136 | $service .= '/versions/' . $version; 137 | } 138 | 139 | if($id == null) { 140 | $data = array('delete_all'=>true); 141 | $json = json_encode($data); 142 | }else{ 143 | $json = ''; 144 | } 145 | 146 | $result = $this->SendDelete($json, $service); 147 | 148 | return (Response::GetJson($result)); 149 | } 150 | 151 | /** 152 | * Retrieve a list of models, models by id or models by version/id. 153 | * @param string $id Model ID 154 | * @param string $version Version ID 155 | * @throws \ErrorException 156 | * @return string Json response from ClarifAI. 157 | */ 158 | public function ModelGet($id=null, $version=null) { 159 | if(!$this->IsApiKeySet()) { 160 | throw new \ErrorException('No API Key Set'); 161 | } 162 | 163 | $service = 'models'; 164 | 165 | if(is_array($this->paginate)) { 166 | $service .= '?page='.$this->paginate['page'].'&per_page='.$this->paginate['count']; 167 | } 168 | 169 | $data = array($id); 170 | 171 | if($version != null && $id != null) { 172 | array_push($data, 'versions', $version); 173 | } 174 | 175 | $result = $this->SendGet($data, $service); 176 | 177 | return (Response::GetJson($result)); 178 | } 179 | 180 | /** 181 | * Train a model 182 | * @param string $id Model ID. 183 | * @throws \ErrorException 184 | * @return string Json response from ClarifAI. 185 | */ 186 | public function ModelTrain($id) { 187 | if(!$this->IsApiKeySet()) { 188 | throw new \ErrorException('No API Key Set'); 189 | } 190 | 191 | $service = 'models/'.urlencode($id).'/versions'; 192 | $result = $this->SendPost(null, $service); 193 | 194 | return (Response::GetJson($result)); 195 | } 196 | 197 | /** 198 | * Predict image content based on model passed in. You can pass in the name of existing ClarifAI models which will 199 | * be automatically converted to their hash strings or you can pass in your own hash string from a custom model 200 | * that you have created. 201 | * @param string $model 202 | * @throws \LogicException 203 | * @throws \ErrorException 204 | * @return string Json response from ClarifAI. 205 | */ 206 | public function Predict($model='General') { 207 | if(!isset($this->image) || !is_array($this->image)) { 208 | throw new \LogicException('You must add at least one image via AddImage().'); 209 | } 210 | 211 | if(!$this->IsApiKeySet()) { 212 | throw new \ErrorException('No API Key Set'); 213 | } 214 | 215 | // Custom model handler. 216 | if(!array_key_exists($model, $this->models)) { 217 | $service = 'models/' . urlencode($model) . '/outputs'; 218 | } 219 | else { 220 | $service = 'models/' . $this->models[$model] . '/outputs'; 221 | } 222 | 223 | // Language change requested, but only for public models. Per Clarifai documentatin, language support is only 224 | // available on this public model. 225 | if((isset($this->language)) && ($model=='General' || $model=='aaa03c23b3724a16a56b629203edc62c')) { 226 | $this->image['model']['output_info']['output_config']['language'] = $this->language; 227 | } 228 | 229 | $json = json_encode($this->image); 230 | $result = $this->SendPost($json, $service); 231 | 232 | return (Response::GetJson($result)); 233 | } 234 | 235 | /** 236 | * Add an image(s) to be indexed. 237 | * @throws \LogicException 238 | * @throws \ErrorException 239 | * @return string Json response from ClarifAI. 240 | */ 241 | public function InputsAdd() { 242 | if(!isset($this->image) || !is_array($this->image)) { 243 | throw new \LogicException('You must add at least one image via AddImage().'); 244 | } 245 | 246 | if(!$this->IsApiKeySet()) { 247 | throw new \ErrorException('No API Key Set'); 248 | } 249 | 250 | $service = 'inputs'; 251 | $json = json_encode($this->image); 252 | $result = $this->SendPost($json, $service); 253 | 254 | return (Response::GetJson($result)); 255 | } 256 | 257 | /** 258 | * Update an input by adding or deleting concepts for it. 259 | * The default action is to merge concepts to the given input id(s). 260 | * To delete a concept(s), pass 'remove' as the action variable. 261 | * 262 | * @param string $action Action to be taken on the set concepts. 263 | * @return string Json response from ClarifAI. 264 | */ 265 | public function InputsUpdate($action='merge') { 266 | if(!isset($this->concept) || !is_array($this->concept)) { 267 | throw new \LogicException('You must add at least one concept via AddConcept()'); 268 | } 269 | 270 | if(!$this->IsApiKeySet()) { 271 | throw new \ErrorException('No API Key Set'); 272 | } 273 | 274 | $service = 'inputs'; 275 | $this->concept['action']=$action; 276 | $json = json_encode($this->concept); 277 | 278 | $result = $this->SendPatch($json, $service); 279 | 280 | return (Response::GetJson($result)); 281 | } 282 | 283 | /** 284 | * Delete an image by its ID. You can pass in either a single id as $id={ID1} or 285 | * you can submit an array of id's $id=array({ID1},{ID2},{ID3}). 286 | * @param mixed $id ID of image to delete 287 | * @throws \ErrorException 288 | * @return string Json response from ClarifAI. 289 | */ 290 | public function InputsDelete($id=null) { 291 | if(!$this->IsApiKeySet()) { 292 | throw new \ErrorException('No API Key Set'); 293 | } 294 | 295 | if(is_array($id)) { 296 | //Delete multiple input 297 | $data['ids'] = $id; 298 | $json = json_encode($data); 299 | $service = 'inputs'; 300 | } 301 | elseif($id == null){ 302 | //Delete all inputs 303 | $data = array('delete_all'=>true); 304 | $json = json_encode($data); 305 | $service = 'inputs'; 306 | } 307 | else { 308 | //Delete specific input 309 | $service = 'inputs/'.$id; 310 | $json = ''; 311 | } 312 | 313 | $result = $this->SendDelete($json, $service); 314 | 315 | return (Response::GetJson($result)); 316 | } 317 | 318 | /** 319 | * Return inputs that you have indexed, you may pass an ID to return a specific input. 320 | * @param string $data 321 | * @throws \ErrorException 322 | * @return string Json response from ClarifAI. 323 | */ 324 | public function InputsGet($id=null) { 325 | if(!$this->IsApiKeySet()) { 326 | throw new \ErrorException('No API Key Set'); 327 | } 328 | 329 | $service = 'inputs'; 330 | 331 | if(is_array($this->paginate) && $id===null) { 332 | $service = $service . '?page='.$this->paginate['page'].'&per_page='.$this->paginate['count']; 333 | } 334 | 335 | $data = array($id); 336 | $result = $this->SendGet($data, $service); 337 | 338 | return (Response::GetJson($result)); 339 | } 340 | 341 | /** 342 | * Get the status of your bulk inputs 343 | * @throws \ErrorException 344 | * @return string Json response from ClarifAI. 345 | */ 346 | public function InputsGetStatus() { 347 | if(!$this->IsApiKeySet()) { 348 | throw new \ErrorException('No API Key Set'); 349 | } 350 | 351 | $service = 'inputs'; 352 | $data = array('status'); 353 | $result = $this->SendGet($data, $service); 354 | 355 | return (Response::GetJson($result)); 356 | } 357 | 358 | /** 359 | * Search your indexed images, you may search by concept, user concept, metadata or url. 360 | * the $term variable should only be an array when searching metadata. 361 | * ClarifAI... this search array structure hurts my head. 362 | * @throws InvalidArgumentException 363 | * @throws ErrorException 364 | * @param mixed $term 365 | * @param string $by 366 | * @param bool $exists 367 | * @return string Json response from ClarifAI. 368 | */ 369 | public function Search($term, $by='concept', $exists=true) { 370 | $search_by = array( 371 | 'concept' => array('data_type'=>'concepts', 'direction'=>'output', 'content'=>array(array('name'=>$term, 'value'=>$exists))), 372 | 'user_concept' => array('data_type'=>'concepts', 'direction'=>'input', 'content'=>array(array('name'=>$term, 'value'=>$exists))), 373 | 'meta' => array('data_type'=>'metadata', 'direction'=>'input', 'content'=>array($term[0]=>$term[1])), 374 | 'url' => array('data_type'=>'image', 'direction'=>'input', 'content'=>array('url'=>$term)), 375 | 'image' => array('data_type'=>'image', 'direction'=>'output', 'content'=>array('url'=>$term)) 376 | ); 377 | 378 | // Light validation 379 | if(!array_key_exists($by, $search_by)) { 380 | throw new \InvalidArgumentException('Invalid \'search by\' parameter.'); 381 | } 382 | 383 | if($by == 'meta' && !is_array($term)) { 384 | throw new \InvalidArgumentException('Metadata search requires your search term to be an array of [0]=key, [1]=value.'); 385 | } 386 | 387 | if($by != 'meta' && !is_string($term)) { 388 | throw new \InvalidArgumentException('Search term should be a string.'); 389 | } 390 | 391 | $this->search = array( 392 | 'query'=>array( 393 | 'ands'=>array( 394 | array( 395 | $search_by[$by]['direction']=>array( 396 | 'data'=>array( 397 | $search_by[$by]['data_type']=>$search_by[$by]['content'] 398 | ) 399 | ) 400 | ) 401 | ) 402 | ) 403 | ); 404 | 405 | // If a change is language has been requested. 406 | if(isset($this->language) && $by == 'concept') { 407 | $this->search['query']['language'] = $this->language; 408 | } 409 | 410 | // Dynamically adjust output/input for image search. 411 | if($by === 'image') { 412 | $this->search['query']['ands'][0]['output'] = array('input'=>$this->search['query']['ands'][0]['output']); 413 | } 414 | 415 | if(!$this->IsApiKeySet()) { 416 | throw new \ErrorException('No API Key Set'); 417 | } 418 | 419 | $service = 'searches'; 420 | $json = json_encode($this->search); 421 | //echo $json; 422 | //exit; 423 | $result = $this->SendPost($json, $service); 424 | 425 | return (Response::GetJson($result)); 426 | } 427 | 428 | /** 429 | * Prepare concepts for API call. This stores the concept in $this->concept. Calling AddConcept() 430 | * multiple times will build and array of concepts to be posted. 431 | * 432 | * @param string $id Input ID to modify for this concept. 433 | * @param array $concepts Array of id=>value 434 | * @return null 435 | */ 436 | public function AddConcept($id, $concepts) { 437 | $data = array('id'=>$id, 'data'=>array('concepts'=>array())); 438 | $data['data']['concepts'] = $concepts; 439 | 440 | $this->concept['inputs'][] = $data; 441 | 442 | return null; 443 | } 444 | 445 | /** 446 | * Prepare images for API call. This stores the image and optional data in $this->image. Calling AddImage() 447 | * multiple times will build an array of images to be posted. A maximum of 128 images per post is allowed. 448 | * 449 | * @param string $image url to image or image in bytes. 450 | * @param string $id identifier to use with image when calling "inputs". 451 | * @param array $concept optional concept data, see documentation for structure. 452 | * @param array $metadata optional metadata, see documentation for structure. 453 | * @param array $crop optional image crop data, see documentaiton for structure. 454 | * @throws ErrorException 455 | * @return null 456 | */ 457 | public function AddImage($image, $id='', $concept=array(), $metadata=array(), $crop=array()) { 458 | //Base package format 459 | $data = array('data'=>array('image'=>array())); 460 | 461 | //If id passed in, typically for input commands. 462 | if($id != '') { 463 | $data['id']=$id; 464 | } 465 | 466 | //Is the image a url or bytes 467 | if(filter_var($image, FILTER_VALIDATE_URL) === FALSE) { 468 | $data['data']['image']['base64'] = base64_encode($image); 469 | } else { 470 | $data['data']['image']['url'] = $image; 471 | } 472 | 473 | if(count($concept)) { 474 | $data['data']['concepts'] = array($concept); 475 | } 476 | 477 | if(count($metadata)) { 478 | $data['data']['metadata'] = $metadata; 479 | } 480 | 481 | if(count($crop)) { 482 | $data['data']['image']['crop'] = $crop; 483 | } 484 | 485 | $this->image['inputs'][] = $data; 486 | 487 | if(count($this->image['inputs']) > 128 ) { 488 | throw new \ErrorException('Image input maximum of 128 exceeded.'); 489 | } 490 | 491 | return null; 492 | } 493 | 494 | /** 495 | * Returns json with each image that is currently active within the ImageClient object. 496 | * @return string 497 | */ 498 | public function ShowImage() { 499 | return json_encode($this->image); 500 | } 501 | 502 | /** 503 | * Set the return language to use. 504 | * @param string $language 505 | * @throws \InvalidArgumentException 506 | * @return null 507 | */ 508 | public function SetLanguage($language) { 509 | if(!array_key_exists($language, $this->languages)) { 510 | throw new \InvalidArgumentException('The language code you have requested is invalid.'); 511 | } 512 | 513 | $this->language = $language; 514 | 515 | return null; 516 | } 517 | 518 | /** 519 | * Returns the current set language. 520 | * @return string 521 | */ 522 | public function ShowLanguage() { 523 | if(isset($this->language)) { 524 | return $this->language; 525 | } 526 | 527 | return ''; 528 | } 529 | 530 | /** 531 | * Set pagination for the next call. 532 | * @param int $page 533 | * @param int $count 534 | * @return null 535 | */ 536 | public function Paginate($page, $count) { 537 | $this->paginate = array('page'=>$page, 'count'=>$count); 538 | 539 | return null; 540 | } 541 | 542 | /** 543 | * Move pagination forward one page. 544 | * @throws \LogicException 545 | * @param int $page number of pages to move forward. 546 | * @return null 547 | */ 548 | public function PageForward($page=1) { 549 | if(!is_array($this->paginate)) { 550 | throw new \LogicException('You must initiate paginate first via Paginate()'); 551 | } 552 | 553 | $this->paginate['page'] = $this->paginate['page']+$page; 554 | 555 | return null; 556 | } 557 | 558 | /** 559 | * Move pagination back one page. 560 | * @throws \LogicException 561 | * @return null 562 | */ 563 | public function PageBack($page=1) { 564 | if(!is_array($this->paginate)) { 565 | throw new \LogicException('You must initiate paginate first via Paginate()'); 566 | } 567 | 568 | // Only adjust paginate page if we are not on page 1. 569 | if($this->paginate['page'] > 1) { 570 | $this->paginate['page'] = $this->paginate['page']-$page; 571 | } 572 | 573 | return null; 574 | } 575 | 576 | /** 577 | * Clear/reset $this->image variable after adding an image via AddImage. 578 | * 579 | * @return null 580 | */ 581 | public function ClearImages() { 582 | unset($this->image); 583 | return null; 584 | } 585 | 586 | /** 587 | * Clear/reset $this->concept variable after adding a concept via AddConcept. 588 | * 589 | * @return null 590 | */ 591 | public function ClearConcepts() { 592 | unset($this->concept); 593 | return null; 594 | } 595 | 596 | /** 597 | * Clear/rest $this->pagination. 598 | * @return NULL 599 | */ 600 | public function ClearPagination() { 601 | unset($this->paginate); 602 | return null; 603 | } 604 | } 605 | -------------------------------------------------------------------------------- /src/Response/Response.php: -------------------------------------------------------------------------------- 1 | 6 | * @link https://github.com/PHPfanatic/clarifai 7 | * @version 2.0.1 8 | */ 9 | 10 | class Response 11 | { 12 | /** 13 | * Return a json response. By default ClarifAI returns json. 14 | * @param string $result 15 | * @return string 16 | */ 17 | public static function GetJson($result) { 18 | return $result; 19 | } 20 | 21 | /** 22 | * Return an array. 23 | * @param string $result 24 | * @return array 25 | */ 26 | public static function GetArray($result) { 27 | return json_decode($result, true); 28 | } 29 | 30 | /** 31 | * Return the resulting status code. 32 | * @param string $result 33 | * @return int 34 | */ 35 | public static function GetStatusCode($result) { 36 | $result = json_decode($result, true); 37 | 38 | return $result['status']['code']; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/ImageClientTest.php: -------------------------------------------------------------------------------- 1 | client = new ImageClient($this->apikey); 12 | } 13 | 14 | protected function tearDown() { 15 | unset($this->client); 16 | } 17 | 18 | /** 19 | * Test that adding paginate create the paginate array correctly. 20 | */ 21 | public function testPaginate() { 22 | $this->client->Paginate(1, 20); 23 | $this->assertEquals(1, $this->client->paginate['page'], 'Paginate page was not added.'); 24 | } 25 | 26 | /** 27 | * Test moving pagination forward x pages and verify the count is correct. 28 | */ 29 | public function testPageForward() { 30 | $this->client->Paginate(1, 20); 31 | $this->client->PageForward(5); 32 | 33 | $this->assertEquals(6, $this->client->paginate['page'], 'Page forward did not increment correctly.'); 34 | } 35 | 36 | /** 37 | * Test moving pagination back x pages and verify the count is correct. 38 | */ 39 | public function testPageBack() { 40 | $this->client->Paginate(5, 20); 41 | $this->client->PageBack(2); 42 | 43 | $this->assertEquals(3, $this->client->paginate['page'], 'Page back did not increment correctly.'); 44 | } 45 | 46 | /** 47 | * Test adding an image builds the correct structure. 48 | */ 49 | public function testAddImage() { 50 | $this->client->AddImage('http://phpfanatic.com/projects/clarifai/cat.png'); 51 | $this->assertArrayHasKey('url', $this->client->image['inputs'][0]['data']['image'], 'Adding an image failed.'); 52 | } 53 | 54 | /** 55 | * Test adding multiple images builds array of images. 56 | */ 57 | public function testAddImageMultiple() { 58 | $this->client->AddImage('http://phpfanatic.com/projects/clarifai/cat.png'); 59 | $this->client->AddImage('http://phpfanatic.com/projects/clarifai/cat.png'); 60 | $this->assertCount(2, $this->client->image['inputs'], 'Adding multiple images failed.'); 61 | } 62 | 63 | /** 64 | * Test that adding more than 128 images throws and error. 65 | */ 66 | public function testAddMaxImage() { 67 | $this->expectException(ErrorException::class); 68 | for ($i = 0; $i < 130; $i++) { 69 | $this->client->AddImage('http://phpfanatic.com/projects/clarifai/cat.png'); 70 | } 71 | } 72 | 73 | /** 74 | * Test adding multiple concepts builds array of concepts. 75 | */ 76 | public function testAddConcept() { 77 | $this->client->AddConcept('testid1', array(array('id'=>'dog', 'value'=>true))); 78 | $this->client->AddConcept('testid2', array(array('id'=>'dog', 'value'=>false))); 79 | $this->assertCount(2, $this->client->concept['inputs'], 'Adding multiple concepts failed.'); 80 | } 81 | 82 | /** 83 | * Test sending predict without an image. 84 | */ 85 | public function testPredictNoImage() { 86 | $this->expectException(LogicException::class); 87 | $this->client->Predict(); 88 | } 89 | 90 | /** 91 | * Validate that setting language works as intended. 92 | */ 93 | public function testSetLanguage() { 94 | $this->client->SetLanguage('es'); 95 | $result = $this->client->ShowLanguage(); 96 | $this->assertEquals('es', $result, 'Language modifier was not set.'); 97 | } 98 | 99 | /** 100 | * Validate that the client version is of the correct format. 101 | */ 102 | public function testGetClientVersion() { 103 | $clientversion = $this->client->GetClientVersion(); 104 | $this->assertRegExp('/^\d{1,3}[.]\d{1,3}[.]\d{1,3}$/', $clientversion); 105 | } 106 | } --------------------------------------------------------------------------------