'/download/clients', 'PrintNode\Download' => '/download/client', 'PrintNode\ApiKey' => '/account/apikey', 'PrintNode\Account' => '/account', 'PrintNode\Tag' => '/account/tag', 'PrintNode\Whoami' => '/whoami', 'PrintNode\Computer' => '/computers', 'PrintNode\Printer' => '/printers', 'PrintNode\PrintJob' => '/printjobs', ); /** * Map method names used by __call to entity names * @var string[] */ private $methodNameEntityMap = array( 'Clients' => 'PrintNode\Client', 'Downloads' => 'PrintNode\Download', 'ApiKeys' => 'PrintNode\ApiKey', 'Account' => 'PrintNode\Account', 'Tags' => 'PrintNode\Tag', 'Whoami' => 'PrintNode\Whoami', 'Computers' => 'PrintNode\Computer', 'Printers' => 'PrintNode\Printer', 'PrintJobs' => 'PrintNode\PrintJob', ); /** * Constructor * @param Credentials $credentials * @param mixed $endPointUrls * @param mixed $methodNameEntityMap * @param int $offset * @param int $limit * @return Request */ public function __construct(Credentials $credentials, array $endPointUrls = array(), array $methodNameEntityMap = array(), $offset = 0, $limit = 10) { if (!function_exists('curl_init')) { throw new \RuntimeException('Function curl_init does not exist.'); } $this->credentials = $credentials; $this->headers = $credentials->getHeaders(); if ($endPointUrls) { $this->endPointUrls = $endPointUrls; } if ($methodNameEntityMap) { $this->methodNameEntityMap = $methodNameEntityMap; } $this->makeEndPointUrls(); $this->setOffset($offset); $this->setLimit($limit); } /** * Get API EndPoint URL from an entity name * @param mixed $entityName * @return string */ private function makeEndPointUrls() { $endPointUrls; foreach ($this->methodNameEntityMap as $classes) { $endPointUrls[$classes] = $this->apiurl.$this->endPointUrls[$classes]; } $this->endPointUrls = $endPointUrls; } private function getEndPointUrl($entityName) { if (!isset($this->endPointUrls[$entityName])) { throw new \InvalidArgumentException( sprintf( 'Missing endPointUrl for entityName "%s"', $entityName ) ); } return $this->endPointUrls[$entityName]; } /** * Get entity name from __call method name * @param mixed $methodName * @return string */ private function getEntityName($methodName) { if (!preg_match('/^get(.+)$/', $methodName, $matchesArray)) { throw new \BadMethodCallException( sprintf( 'Method %s::%s does not exist', get_class($this), $methodName ) ); } if (!isset($this->methodNameEntityMap[$matchesArray[1]])) { throw new \BadMethodCallException( sprintf( '%s is missing an methodNameMap entry for %s', get_class($this), $methodName ) ); } return $this->methodNameEntityMap[$matchesArray[1]]; } /** * Initialise cURL with the options we need * to communicate successfully with API URL. * @param void * @return resource */ private function curlInit() { $curlHandle = curl_init(); curl_setopt($curlHandle, CURLOPT_ENCODING, 'gzip,deflate'); curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true); curl_setopt($curlHandle, CURLOPT_VERBOSE, false); curl_setopt($curlHandle, CURLOPT_HEADER, true); curl_setopt($curlHandle, CURLOPT_USERPWD, (string)$this->credentials); curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curlHandle, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($curlHandle, CURLOPT_TIMEOUT, 4); curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, true); return $curlHandle; } /** * Execute cURL request using the specified API EndPoint * @param mixed $curlHandle * @param mixed $endPointUrl * @return Response */ private function curlExec($curlHandle, $endPointUrl) { curl_setopt($curlHandle, CURLOPT_URL, $endPointUrl); if (($response = @curl_exec($curlHandle)) === false) { throw new \RuntimeException( sprintf( 'cURL Error (%d): %s', curl_errno($curlHandle), curl_error($curlHandle) ) ); } curl_close($curlHandle); $response_parts = explode("\r\n\r\n", $response); $content = array_pop($response_parts); $headers = explode("\r\n", array_pop($response_parts)); return new Response($endPointUrl, $content, $headers); } /** * Make a GET request using cURL * @param mixed $endPointUrl * @return Response */ private function curlGet($endPointUrl) { $curlHandle = $this->curlInit(); curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $this->headers); return $this->curlExec( $curlHandle, $endPointUrl ); } private function curlDelete($endPointUrl) { $curlHandle = $this->curlInit(); curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, 'DELETE'); curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $this->headers); return $this->curlExec( $curlHandle, $endPointUrl ); } /** * Apply offset and limit to a end point URL. * @param mixed $endPointUrl * @return string */ private function applyOffsetLimit($endPointUrl) { $endPointUrlArray = parse_url($endPointUrl); if (!isset($endPointUrlArray['query'])) { $endPointUrlArray['query'] = null; } parse_str($endPointUrlArray['query'], $queryStringArray); $queryStringArray['offset'] = $this->offset; $queryStringArray['limit'] = min(max(1, $this->limit), 500); $endPointUrlArray['query'] = http_build_query($queryStringArray, null, '&'); $endPointUrl = (isset($endPointUrlArray['scheme'])) ? "{$endPointUrlArray['scheme']}://" : ''; $endPointUrl.= (isset($endPointUrlArray['host'])) ? "{$endPointUrlArray['host']}" : ''; $endPointUrl.= (isset($endPointUrlArray['port'])) ? ":{$endPointUrlArray['port']}" : ''; $endPointUrl.= (isset($endPointUrlArray['path'])) ? "{$endPointUrlArray['path']}" : ''; $endPointUrl.= (isset($endPointUrlArray['query'])) ? "?{$endPointUrlArray['query']}" : ''; return $endPointUrl; } /** * Make a POST/PUT/DELETE request using cURL * @param Entity $entity * @param mixed $httpMethod * @return Response */ private function curlSend() { $arguments = func_get_args(); $httpMethod = array_shift($arguments); $data = array_shift($arguments); $endPointUrl = array_shift($arguments); $curlHandle = $this->curlInit(); curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, $httpMethod); curl_setopt($curlHandle, CURLOPT_POSTFIELDS, (string)$data); curl_setopt($curlHandle, CURLOPT_HTTPHEADER, array_merge(array('Content-Type: application/json'), $this->headers)); return $this->curlExec( $curlHandle, $endPointUrl ); } /** * Set the offset for GET requests * @param mixed $offset */ public function setOffset($offset) { if (!ctype_digit($offset) && !is_int($offset)) { throw new \InvalidArgumentException('offset should be a number'); } $this->offset = $offset; } /** * Set the limit for GET requests * @param mixed $limit */ public function setLimit($limit) { if (!ctype_digit($limit) && !is_int($limit)) { throw new \InvalidArgumentException('limit should be a number'); } $this->limit = $limit; } /** * Delete an ApiKey for a child account * @param string $apikey * @return Response * */ public function deleteApiKey($apikey) { $endPointUrl = $this->apiurl."/account/apikey/".$apikey; $response = $this->curlDelete($endPointUrl); return $response; } /** * Delete a tag for a child account * @param string $tag * @return Response * */ public function deleteTag($tag) { $endPointUrl = $this->apiurl."/account/tag/".$tag; $response = $this->curlDelete($endPointUrl); return $response; } /** * Delete a child account * MUST have $this->headers set to run. * @return Response * */ public function deleteAccount() { if (!isset($this->headers)) { throw new Exception( sprintf( 'No child authentication set - cannot delete your own account.' ) ); } $endPointUrl = $this->apiurl."/account/"; $response = $this->curlDelete($endPointUrl); return $response; } /** * Returns a client key. * @param string $uuid * @param string $edition * @param string $version * @return Resposne * */ public function getClientKey($uuid, $edition, $version) { $endPointUrl = $this->apiurl."/client/key/".$uuid."?edition=".$edition."&version=".$version; $response = $this->curlGet($endPointUrl); return $response; } /** * Gets print job states. * @param string $printjobId OPTIONAL:if unset gives states relative to all printjobs. * @return Entity[] * */ public function getPrintJobStates() { $arguments = func_get_args(); if (count($arguments) > 1) { throw new InvalidArgumentException( sprintf( 'Too many arguments given to getPrintJobsStates.' ) ); } $endPointUrl = $this->apiurl."/printjobs/"; if (count($arguments) == 0) { $endPointUrl.= 'states/'; } else { $arg_1 = array_shift($arguments); $endPointUrl.= $arg_1.'/states/'; } $response = $this->curlGet($endPointUrl); if ($response->getStatusCode() != '200') { throw new \RuntimeException( sprintf( 'HTTP Error (%d): %s', $response->getStatusCode(), $response->getStatusMessage() ) ); } return Entity::makeFromResponse("PrintNode\State", json_decode($response->getContent())); } /** * Gets PrintJobs relative to a printer. * @param string $printerIdSet set of printer ids to find PrintJobs relative to * @param string $printJobId OPTIONAL: set of PrintJob ids relative to the printer. * @return Entity[] * */ public function getPrintJobsByPrinters() { $arguments = func_get_args(); if (count($arguments) > 2) { throw new InvalidArgumentException( sprintf( 'Too many arguments given to getPrintJobsByPrinters.' ) ); } $endPointUrl = $this->apiurl."/printers/"; $arg_1 = array_shift($arguments); $endPointUrl.= $arg_1.'/printjobs/'; foreach ($arguments as $argument) { $endPointUrl.= $argument; } $response = $this->curlGet($endPointUrl); if ($response->getStatusCode() != '200') { throw new \RuntimeException( sprintf( 'HTTP Error (%d): %s', $response->getStatusCode(), $response->getStatusMessage() ) ); } return Entity::makeFromResponse("PrintNode\PrintJob", json_decode($response->getContent())); } /** * Gets scales relative to a computer. * @param string $computerId id of computer to find scales * @return Entity[] * */ public function getScales(string $computerId) { $endPointUrl = $this->apiurl."/computer/"; $endPointUrl.= $computerId; $endPointUrl.= '/scales'; $response = $this->curlGet($endPointUrl); if ($response->getStatusCode() != '200') { throw new \RuntimeException( sprintf( 'HTTP Error (%d): %s', $response->getStatusCode(), $response->getStatusMessage() ) ); } return Entity::makeFromResponse("PrintNode\Scale", json_decode($response->getContent())); } /** * Get printers relative to a computer. * @param string $computerIdSet set of computer ids to find printers relative to * @param string $printerIdSet OPTIONAL: set of printer ids only found in the set of computers. * @return Entity[] * */ public function getPrintersByComputers() { $arguments = func_get_args(); if (count($arguments) > 2) { throw new InvalidArgumentException( sprintf( 'Too many arguments given to getPrintersByComputers.' ) ); } $endPointUrl = $this->apiurl."/computers/"; $arg_1 = array_shift($arguments); $endPointUrl.= $arg_1.'/printers/'; foreach ($arguments as $argument) { $endPointUrl.= $argument; } $response = $this->curlGet($endPointUrl); if ($response->getStatusCode() != '200') { throw new \RuntimeException( sprintf( 'HTTP Error (%d): %s', $response->getStatusCode(), $response->getStatusMessage() ) ); } return Entity::makeFromResponse("PrintNode\Printer", json_decode($response->getContent())); } /** * Map method names getComputers, getPrinters and getPrintJobs to entities * @param mixed $methodName * @param mixed $arguments * @return Entity[] */ public function __call($methodName, $arguments) { $entityName = $this->getEntityName($methodName); $endPointUrl = $this->getEndPointUrl($entityName); if (count($arguments) > 0) { $arguments = array_shift($arguments); if (!is_string($arguments)) { throw new InvalidArgumentException( sprintf( 'Invalid argument type passed to %s. Expecting a string got %s', $methodName, gettype($arguments) ) ); } $endPointUrl = sprintf( '%s/%s', $endPointUrl, $arguments ); } else { $endPointUrl = sprintf( '%s', $endPointUrl ); } $response = $this->curlGet($endPointUrl); if ($response->getStatusCode() != '200') { throw new \RuntimeException( sprintf( 'HTTP Error (%d): %s', $response->getStatusCode(), $response->getStatusMessage() ) ); } return Entity::makeFromResponse($entityName, json_decode($response->getContent())); } /** * PATCH (update) the specified entity * @param Entity $entity * @return Response * */ public function patch(Entity $entity) { if (!($entity instanceof Entity)) { throw new InvalidArgumentException( sprintf( 'Invalid argument type passed to patch. Expecting Entity got %s', gettype($entity) ) ); } $endPointUrl = $this->getEndPointUrl(get_class($entity)); if (method_exists($entity, 'endPointUrlArg')) { $endPointUrl.= '/'.$entity->endPointUrlArg(); } if (method_exists($entity, 'formatForPatch')) { $entity = $entity->formatForPatch(); } return $this->curlSend('PATCH', $entity, $endPointUrl); } /** * POST (create) the specified entity * @param Entity $entity * @return Response */ public function post(Entity $entity) { if (!($entity instanceof Entity)) { throw new InvalidArgumentException( sprintf( 'Invalid argument type passed to patch. Expecting Entity got %s', gettype($entity) ) ); } $endPointUrl = $this->getEndPointUrl(get_class($entity)); if (method_exists($entity, 'endPointUrlArg')) { $endPointUrl.= '/'.$entity->endPointUrlArg(); } if (method_exists($entity, 'formatForPost')){ $entity = $entity->formatForPost(); } return $this->curlSend('POST', $entity, $endPointUrl); } /** * PUT (update) the specified entity * @param Entity $entity * @return Response */ public function put() { $arguments = func_get_args(); $entity = array_shift($arguments); if (!($entity instanceof Entity)) { throw new InvalidArgumentException( sprintf( 'Invalid argument type passed to patch. Expecting Entity got %s', gettype($entity) ) ); } $endPointUrl = $this->getEndPointUrl(get_class($entity)); foreach ($arguments as $argument) { $endPointUrl.= '/'.$argument; } return $this->curlSend('PUT', $entity, $endPointUrl); } /** * DELETE (delete) the specified entity * @param Entity $entity * @return Response */ public function delete(Entity $entity) { $endPointUrl = $this->getEndPointUrl(get_class($entity)); if (method_exists($entity, 'endPointUrlArg')) { $endPointUrl.= '/'.$entity->endPointUrlArg(); } return $this->curlDelete($endPointUrl); } }