PageRenderTime 24ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/mls-project/php/SiFrameworks/phrets2.2/vendor/troydavisson/phrets/src/Session.php

https://bitbucket.org/amruthaviswanath/mls-qa-tool
PHP | 492 lines | 291 code | 61 blank | 140 comment | 31 complexity | 2a31c5b9ac329eb6d108e188aeb89b6e MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.0
  1. <?php namespace PHRETS;
  2. use GuzzleHttp\Client;
  3. use GuzzleHttp\Cookie\CookieJar;
  4. use GuzzleHttp\Cookie\CookieJarInterface;
  5. use Illuminate\Support\Collection;
  6. use PHRETS\Exceptions\CapabilityUnavailable;
  7. use PHRETS\Exceptions\MetadataNotFound;
  8. use PHRETS\Exceptions\MissingConfiguration;
  9. use PHRETS\Exceptions\RETSException;
  10. use PHRETS\Http\Client as PHRETSClient;
  11. use PHRETS\Interpreters\GetObject;
  12. use PHRETS\Interpreters\Search;
  13. use PHRETS\Models\Bulletin;
  14. class Session
  15. {
  16. /** @var Configuration */
  17. protected $configuration;
  18. /** @var Capabilities */
  19. protected $capabilities;
  20. /** @var Client */
  21. protected $client;
  22. /** @var \PSR\Log\LoggerInterface */
  23. protected $logger;
  24. protected $rets_session_id;
  25. protected $cookie_jar;
  26. protected $last_request_url;
  27. /** @var \GuzzleHttp\Message\ResponseInterface */
  28. protected $last_response;
  29. public function __construct(Configuration $configuration)
  30. {
  31. // save the configuration along with this session
  32. $this->configuration = $configuration;
  33. // start up our Guzzle HTTP client
  34. $this->client = PHRETSClient::make();
  35. $this->cookie_jar = new CookieJar;
  36. // set the authentication as defaults to use for the entire client
  37. $this->client->setDefaultOption(
  38. 'auth',
  39. [
  40. $configuration->getUsername(),
  41. $configuration->getPassword(),
  42. $configuration->getHttpAuthenticationMethod()
  43. ]
  44. );
  45. $this->client->setDefaultOption(
  46. 'headers',
  47. [
  48. 'User-Agent' => $configuration->getUserAgent(),
  49. 'RETS-Version' => $configuration->getRetsVersion()->asHeader(),
  50. 'Accept-Encoding' => 'gzip',
  51. 'Accept' => '*/*',
  52. ]
  53. );
  54. // disable following 'Location' header (redirects) automatically
  55. if ($this->configuration->readOption('disable_follow_location')) {
  56. $this->client->setDefaultOption('allow_redirects', false);
  57. }
  58. // start up the Capabilities tracker and add Login as the first one
  59. $this->capabilities = new Capabilities;
  60. $this->capabilities->add('Login', $configuration->getLoginUrl());
  61. }
  62. /**
  63. * PSR-3 compatible logger can be attached here
  64. *
  65. * @param $logger
  66. */
  67. public function setLogger($logger)
  68. {
  69. $this->logger = $logger;
  70. $this->debug("Loading " . get_class($logger) . " logger");
  71. }
  72. /**
  73. * @throws Exceptions\CapabilityUnavailable
  74. * @throws Exceptions\MissingConfiguration
  75. * @returns Bulletin
  76. */
  77. public function Login()
  78. {
  79. if (!$this->configuration or !$this->configuration->valid()) {
  80. throw new MissingConfiguration("Cannot issue Login without a valid configuration loaded");
  81. }
  82. $response = $this->request('Login');
  83. $parser = $this->grab('parser.login');
  84. $parser->parse($response->xml()->{'RETS-RESPONSE'}->__toString());
  85. foreach ($parser->getCapabilities() as $k => $v) {
  86. $this->capabilities->add($k, $v);
  87. }
  88. $bulletin = new Bulletin;
  89. if ($this->capabilities->get('Action')) {
  90. $response = $this->request('Action');
  91. $bulletin->setBody($response->getBody()->__toString());
  92. return $bulletin;
  93. } else {
  94. return $bulletin;
  95. }
  96. }
  97. /**
  98. * @param $resource
  99. * @param $type
  100. * @param $content_id
  101. * @param int $location
  102. * @return \PHRETS\Models\Object
  103. */
  104. public function GetPreferredObject($resource, $type, $content_id, $location = 0)
  105. {
  106. $collection = $this->GetObject($resource, $type, $content_id, '0', $location);
  107. return $collection->first();
  108. }
  109. /**
  110. * @param $resource
  111. * @param $type
  112. * @param $content_ids
  113. * @param string $object_ids
  114. * @param int $location
  115. * @return Collection
  116. * @throws Exceptions\CapabilityUnavailable
  117. */
  118. public function GetObject($resource, $type, $content_ids, $object_ids = '*', $location = 0)
  119. {
  120. $request_id = GetObject::ids($content_ids, $object_ids);
  121. $response = $this->request(
  122. 'GetObject',
  123. [
  124. 'query' => [
  125. 'Resource' => $resource,
  126. 'Type' => $type,
  127. 'ID' => implode(',', $request_id),
  128. 'Location' => $location,
  129. ]
  130. ]
  131. );
  132. if (preg_match('/multipart/', $response->getHeader('Content-Type'))) {
  133. $parser = $this->grab('parser.object.multiple');
  134. $collection = $parser->parse($response);
  135. } else {
  136. $collection = new Collection;
  137. $parser = $this->grab('parser.object.single');
  138. $object = $parser->parse($response);
  139. $collection->push($object);
  140. }
  141. return $collection;
  142. }
  143. /**
  144. * @return Models\Metadata\System
  145. * @throws Exceptions\CapabilityUnavailable
  146. */
  147. public function GetSystemMetadata()
  148. {
  149. return $this->MakeMetadataRequest('METADATA-SYSTEM', 0, 'metadata.system');
  150. }
  151. /**
  152. * @param string $resource_id
  153. * @throws Exceptions\CapabilityUnavailable
  154. * @throws Exceptions\MetadataNotFound
  155. * @return Collection|\PHRETS\Models\Metadata\Resource
  156. */
  157. public function GetResourcesMetadata($resource_id = null)
  158. {
  159. $result = $this->MakeMetadataRequest('METADATA-RESOURCE', 0, 'metadata.resource');
  160. if ($resource_id) {
  161. foreach ($result as $r) {
  162. if ($r->getResourceID() == $resource_id) {
  163. return $r;
  164. }
  165. }
  166. throw new MetadataNotFound("Requested '{$resource_id}' resource metadata does not exist");
  167. }
  168. return $result;
  169. }
  170. /**
  171. * @param $resource_id
  172. * @return mixed
  173. * @throws Exceptions\CapabilityUnavailable
  174. */
  175. public function GetClassesMetadata($resource_id)
  176. {
  177. return $this->MakeMetadataRequest('METADATA-CLASS', $resource_id, 'metadata.class');
  178. }
  179. /**
  180. * @param $resource_id
  181. * @param $class_id
  182. * @param string $keyed_by
  183. * @return \Illuminate\Support\Collection|\PHRETS\Models\Metadata\Table[]
  184. * @throws Exceptions\CapabilityUnavailable
  185. */
  186. public function GetTableMetadata($resource_id, $class_id, $keyed_by = 'SystemName')
  187. {
  188. return $this->MakeMetadataRequest('METADATA-TABLE', $resource_id . ':' . $class_id, 'metadata.table', $keyed_by);
  189. }
  190. /**
  191. * @param $resource_id
  192. * @return mixed
  193. * @throws Exceptions\CapabilityUnavailable
  194. */
  195. public function GetObjectMetadata($resource_id)
  196. {
  197. return $this->MakeMetadataRequest('METADATA-OBJECT', $resource_id, 'metadata.object');
  198. }
  199. /**
  200. * @param $resource_id
  201. * @param $lookup_name
  202. * @return mixed
  203. * @throws Exceptions\CapabilityUnavailable
  204. */
  205. public function GetLookupValues($resource_id, $lookup_name)
  206. {
  207. return $this->MakeMetadataRequest('METADATA-LOOKUP_TYPE', $resource_id . ':' . $lookup_name, 'metadata.lookuptype');
  208. }
  209. /**
  210. * @param $type
  211. * @param $id
  212. * @param $parser
  213. * @param null $keyed_by
  214. * @throws Exceptions\CapabilityUnavailable
  215. * @return mixed
  216. */
  217. protected function MakeMetadataRequest($type, $id, $parser, $keyed_by = null)
  218. {
  219. $response = $this->request(
  220. 'GetMetadata',
  221. [
  222. 'query' => [
  223. 'Type' => $type,
  224. 'ID' => $id,
  225. 'Format' => 'STANDARD-XML',
  226. ]
  227. ]
  228. );
  229. $parser = $this->grab('parser.' . $parser);
  230. return $parser->parse($this, $response, $keyed_by);
  231. }
  232. /**
  233. * @param $resource_id
  234. * @param $class_id
  235. * @param $dmql_query
  236. * @param array $optional_parameters
  237. * @return \PHRETS\Models\Search\Results
  238. * @throws Exceptions\CapabilityUnavailable
  239. */
  240. public function Search($resource_id, $class_id, $dmql_query, $optional_parameters = [], $recursive = false)
  241. {
  242. $dmql_query = Search::dmql($dmql_query);
  243. $defaults = [
  244. 'SearchType' => $resource_id,
  245. 'Class' => $class_id,
  246. 'Query' => $dmql_query,
  247. 'QueryType' => 'DMQL2',
  248. 'Count' => 1,
  249. 'Format' => 'COMPACT-DECODED',
  250. 'Limit' => 99999999,
  251. 'StandardNames' => 0,
  252. ];
  253. $parameters = array_merge($defaults, $optional_parameters);
  254. // if the Select parameter given is an array, format it as it needs to be
  255. if (array_key_exists('Select', $parameters) and is_array($parameters['Select'])) {
  256. $parameters['Select'] = implode(',', $parameters['Select']);
  257. }
  258. $response = $this->request(
  259. 'Search',
  260. [
  261. 'query' => $parameters
  262. ]
  263. );
  264. if ($recursive) {
  265. $parser = $this->grab('parser.search.recursive');
  266. } else {
  267. $parser = $this->grab('parser.search');
  268. }
  269. return $parser->parse($this, $response, $parameters);
  270. }
  271. /**
  272. * @return bool
  273. * @throws Exceptions\CapabilityUnavailable
  274. */
  275. public function Disconnect()
  276. {
  277. $response = $this->request('Logout');
  278. return true;
  279. }
  280. /**
  281. * @param $capability
  282. * @param array $options
  283. * @throws Exceptions\CapabilityUnavailable
  284. * @throws Exceptions\RETSException
  285. * @return \GuzzleHttp\Message\ResponseInterface
  286. */
  287. protected function request($capability, $options = [])
  288. {
  289. $url = $this->capabilities->get($capability);
  290. if (!$url) {
  291. throw new CapabilityUnavailable("'{$capability}' tried but no valid endpoint was found. Did you forget to Login()?");
  292. }
  293. if (!array_key_exists('headers', $options)) {
  294. $options['headers'] = [];
  295. }
  296. // Guzzle 5 changed the order that headers are added to the request, so we'll do this manually
  297. $options['headers'] = array_merge($this->client->getDefaultOption('headers'), $options['headers']);
  298. // user-agent authentication
  299. if ($this->configuration->getUserAgentPassword()) {
  300. $ua_digest = $this->configuration->userAgentDigestHash($this);
  301. $options['headers'] = array_merge($options['headers'], ['RETS-UA-Authorization' => 'Digest ' . $ua_digest]);
  302. }
  303. $options = array_merge($options, ['cookies' => $this->cookie_jar]);
  304. $this->debug("Sending HTTP Request for {$url} ({$capability})", $options);
  305. if (array_key_exists('query', $options)) {
  306. $this->last_request_url = $url . '?' . \http_build_query($options['query']);
  307. } else {
  308. $this->last_request_url = $url;
  309. }
  310. /** @var \GuzzleHttp\Message\ResponseInterface $response */
  311. if ($this->configuration->readOption('use_post_method')) {
  312. $this->debug('Using POST method per use_post_method option');
  313. $query = (array_key_exists('query', $options)) ? $options['query'] : null;
  314. $response = $this->client->post($url, array_merge($options, ['body' => $query]));
  315. } else {
  316. $response = $this->client->get($url, $options);
  317. }
  318. $this->last_response = $response;
  319. $cookie = $response->getHeader('Set-Cookie');
  320. if ($cookie) {
  321. if (preg_match('/RETS-Session-ID\=(.*?)(\;|\s+|$)/', $cookie, $matches)) {
  322. $this->rets_session_id = $matches[1];
  323. }
  324. }
  325. if ($response->getHeader('Content-Type') == 'text/xml' and $capability != 'GetObject') {
  326. $xml = $response->xml();
  327. if ($xml and isset($xml['ReplyCode'])) {
  328. $rc = (string)$xml['ReplyCode'];
  329. // 20201 - No records found - not exception worthy in my mind
  330. if ($rc != "0" and $rc != "20201") {
  331. throw new RETSException($xml['ReplyText'], (int)$xml['ReplyCode']);
  332. }
  333. }
  334. }
  335. $this->debug('Response: HTTP ' . $response->getStatusCode());
  336. return $response;
  337. }
  338. /**
  339. * @return string
  340. */
  341. public function getLoginUrl()
  342. {
  343. return $this->capabilities->get('Login');
  344. }
  345. /**
  346. * @return Capabilities
  347. */
  348. public function getCapabilities()
  349. {
  350. return $this->capabilities;
  351. }
  352. /**
  353. * @return Configuration
  354. */
  355. public function getConfiguration()
  356. {
  357. return $this->configuration;
  358. }
  359. /**
  360. * @param $message
  361. * @param array $context
  362. */
  363. public function debug($message, $context = [])
  364. {
  365. if ($this->logger) {
  366. if (!is_array($context)) {
  367. $context = [$context];
  368. }
  369. $this->logger->debug($message, $context);
  370. }
  371. }
  372. /**
  373. * @return CookieJarInterface
  374. */
  375. public function getCookieJar()
  376. {
  377. return $this->cookie_jar;
  378. }
  379. /**
  380. * @param CookieJarInterface $cookie_jar
  381. * @return $this
  382. */
  383. public function setCookieJar(CookieJarInterface $cookie_jar)
  384. {
  385. $this->cookie_jar = $cookie_jar;
  386. return $this;
  387. }
  388. /**
  389. * @return \GuzzleHttp\Event\EmitterInterface
  390. */
  391. public function getEventEmitter()
  392. {
  393. return $this->client->getEmitter();
  394. }
  395. /**
  396. * @return string
  397. */
  398. public function getLastRequestURL()
  399. {
  400. return $this->last_request_url;
  401. }
  402. /**
  403. * @return string
  404. */
  405. public function getLastResponse()
  406. {
  407. return (string)$this->last_response->getBody();
  408. }
  409. /**
  410. * @return Client
  411. */
  412. public function getClient()
  413. {
  414. return $this->client;
  415. }
  416. /**
  417. * @return mixed
  418. */
  419. public function getRetsSessionId()
  420. {
  421. return $this->rets_session_id;
  422. }
  423. /**
  424. * @param $component
  425. * @return mixed
  426. */
  427. protected function grab($component)
  428. {
  429. return $this->configuration->getStrategy()->provide($component);
  430. }
  431. }