/** * Get the class name for the Model. Purely based on the name property. * * @param bool $with_ns * @return mixed */ public function getClassName($with_ns = false) { $class_name = Helpers::singularize($this->getName()); if ($with_ns) { return sprintf('%s\\%s', $this->getNamespace(), $class_name); } else { return $class_name; } }
public function send() { //Sign the request - this just sets the Authorization header $this->app->getOAuthClient()->sign($this); // configure curl $ch = curl_init(); curl_setopt_array($ch, $this->app->getConfig('curl')); curl_setopt($ch, CURLINFO_HEADER_OUT, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->getMethod()); if (isset($this->body)) { curl_setopt($ch, CURLOPT_POSTFIELDS, $this->body); } //build header array. Don't provide glue so it'll return the array itself. //Maybe could be a but cleaner but nice to reuse code. $header_array = Helpers::flattenAssocArray($this->getHeaders(), '%s: %s'); curl_setopt($ch, CURLOPT_HTTPHEADER, $header_array); $full_uri = $this->getUrl()->getFullURL(); //build parameter array - the only time there's a post body is with the XML, //only escape at this point $query_string = Helpers::flattenAssocArray($this->getParameters(), '%s=%s', '&', true); if (strlen($query_string) > 0) { $full_uri .= "?{$query_string}"; } curl_setopt($ch, CURLOPT_URL, $full_uri); if ($this->method === self::METHOD_POST || $this->method === self::METHOD_PUT) { curl_setopt($ch, CURLOPT_POST, true); } $response = curl_exec($ch); $info = curl_getinfo($ch); if ($response === false) { throw new Exception('Curl error: ' . curl_error($ch)); } $this->response = new Response($this, $response, $info); $this->response->parse(); return $this->response; }
/** * Function to save properties directly which do not update via a POST * * This is called automatically from the save method for things like adding contacts to ContactGroups * * @param Object $object * @throws Exception */ private function savePropertiesDirectly(Object $object) { foreach ($object::getProperties() as $property_name => $meta) { if ($meta[Object::KEY_SAVE_DIRECTLY] && $object->isPropertyDirty($property_name)) { //Then actually save $property_objects = $object->{$property_name}; $property_type = get_class(current($property_objects)); $url = new URL($this, sprintf('%s/%s/%s', $object::getResourceURI(), $object->getGUID(), $property_type::getResourceURI())); $request = new Request($this, $url, Request::METHOD_PUT); $property_array = array(); foreach ($property_objects as $property_object) { $property_array[] = $property_object->toStringArray(); } $root_node_name = Helpers::pluralize($property_type::getRootNodeName()); $request->setBody(Helpers::arrayToXML(array($root_node_name => $property_array))); $request->send(); $response = $request->getResponse(); foreach ($response->getElements() as $element_index => $element) { if ($response->getErrorsForElement($element_index) === null) { $property_objects[$element_index]->fromStringArray($element); $property_objects[$element_index]->setClean(); } } //Set it clean so the following save might have nothing to do. $object->setClean($property_name); } } }
public function parseXML() { $sxml = new SimpleXMLElement($this->response_body); // For lack of a better way to find the elements returned (every time) // XML has an array 2 levels deep due to its non-unique key nature. /** @var SimpleXMLElement $root_child */ foreach ($sxml as $child_index => $root_child) { switch ($child_index) { case 'ErrorNumber': $this->root_error['code'] = (string) $root_child; break; case 'Type': $this->root_error['type'] = (string) $root_child; break; case 'Message': $this->root_error['message'] = (string) $root_child; break; case 'Payslip': case 'PayItems': // some xero endpoints are 1D so we can parse them straight away $this->elements[] = Helpers::XMLToArray($root_child); break; default: //Happy to make the assumption that there will only be one //root node with > than 2D children. foreach ($root_child->children() as $element_index => $element) { $this->elements[] = Helpers::XMLToArray($element); } } } }
/** * @return string */ public function getNameSingular() { return \XeroPHP\Helpers::singularize($this->getName()); }
/** * Get the Signature Base String for signing. This is basically just all params (including the generated oauth ones) * ordered by key, then concatenated with the method and URL * GET&https%3A%2F%2Fapi.xero.com%2Fapi.xro%2F2.0%2FContacts&oauth_consumer etc. * * @return string */ public function getSBS() { $oauth_params = $this->getOAuthParams(); $request_params = $this->request->getParameters(); $sbs_params = array_merge($request_params, $oauth_params); //Params need sorting so signing order is the same ksort($sbs_params); $sbs_string = Helpers::flattenAssocArray($sbs_params, '%s=%s', '&', true); $url = $this->request->getUrl()->getFullURL(); //Every second thing seems to need escaping! return sprintf('%s&%s&%s', $this->request->getMethod(), Helpers::escape($url), Helpers::escape($sbs_string)); }
/** * Search for an entity based on a term. Search by class name, FQ class name - both as-is, plural and singular * * Crude. * * @param $key * @param string $namespace_hint * @return null */ public function searchByKey($key, $namespace_hint = '') { if (!$this->isIndexed()) { $this->buildSearchIndex(); } //Yuck $plural_key = Helpers::pluralize($key); $singular_key = Helpers::singularize($key); $ns_key = sprintf('%s\\%s', $namespace_hint, $key); $plural_ns_key = Helpers::pluralize($ns_key); $singular_ns_key = Helpers::singularize($ns_key); if (isset($this->search_keys[$ns_key])) { return $this->search_keys[$ns_key]; } elseif (isset($this->search_keys[$plural_ns_key])) { return $this->search_keys[$plural_ns_key]; } elseif (isset($this->search_keys[$singular_ns_key])) { return $this->search_keys[$singular_ns_key]; } elseif (isset($this->search_keys[$key])) { return $this->search_keys[$key]; } elseif (isset($this->search_keys[$plural_key])) { return $this->search_keys[$plural_key]; } elseif (isset($this->search_keys[$singular_key])) { return $this->search_keys[$singular_key]; } else { return null; } }
/** * Load an assoc array into the instance of the object $property => $value * $replace_data - replace existing data * * @param $input_array * @param $replace_data */ public function fromStringArray($input_array, $replace_data = false) { foreach (static::getProperties() as $property => $meta) { $type = $meta[self::KEY_TYPE]; $php_type = $meta[self::KEY_PHP_TYPE]; //If set and NOT replace data, continue if (!$replace_data && isset($this->_data[$property])) { continue; } if (!isset($input_array[$property])) { $this->_data[$property] = null; continue; } //Fix for an earlier assumption that the API didn't return more than //two levels of nested objects. //Handles Invoice > Contact > Address etc. in one build. if (is_array($input_array[$property]) && Helpers::isAssoc($input_array[$property]) === false) { $collection = new Collection(); $collection->addAssociatedObject($property, $this); foreach ($input_array[$property] as $assoc_element) { $cast = self::castFromString($type, $assoc_element, $php_type); //Do this here so that you know it's not a static method call to ::castFromString if ($cast instanceof Object) { $cast->addAssociatedObject($property, $this); } $collection->append($cast); } $this->_data[$property] = $collection; } else { $cast = self::castFromString($type, $input_array[$property], $php_type); //Do this here so that you know it's not a static method call to ::castFromString if ($cast instanceof Object) { $cast->addAssociatedObject($property, $this); } $this->_data[$property] = $cast; } } }
/** * Get the prefix for the constant name generation and strip out 'Types' * eg. XERO_USER_ROLE * * @return mixed */ public function getConstantPrefix() { $sane_name = preg_replace('/\\([\\w\\s]+\\)/', '', $this->raw_name); return \XeroPHP\Helpers::singularize(preg_replace('/(\\b(code)s?)/i', '', trim($sane_name))); }