/** * @param array $urls * @param array $pattern * @param bool $failOnError * @return array * @throws HyperParserException */ public function bulkParse(array $urls, array $pattern, $failOnError = true) { $data = []; $promises = []; $httpClient = $this->getHttpClient(); foreach ($urls as $url) { $promises[$url] = $httpClient->requestAsync('GET', $url); } $responses = \GuzzleHttp\Promise\settle($promises)->wait(); foreach ($responses as $url => $response) { $data[$url] = $this->parseResponse($response['value'], $pattern, $failOnError); } return $data; }
public function testSettleFulfillsWithFulfilledAndRejected() { $a = new Promise(); $b = new Promise(); $c = new Promise(); $d = \GuzzleHttp\Promise\settle([$a, $b, $c]); $b->resolve('b'); $c->resolve('c'); $a->reject('a'); P\queue()->run(); $this->assertEquals('fulfilled', $d->getState()); $d->then(function ($value) use(&$result) { $result = $value; }); P\queue()->run(); $this->assertEquals([['state' => 'rejected', 'reason' => 'a'], ['state' => 'fulfilled', 'value' => 'b'], ['state' => 'fulfilled', 'value' => 'c']], $result); }
/** * @Method("GET|POST") * @Route("site.connect", name="api-site.connect") * @Api("Undine\Form\Type\Api\SiteConnectType", streamable=true) * * @param SiteConnectCommand $command * @param callable $stream * * @throws ConstraintViolationException * * @return SiteConnectResult */ public function connectAction(SiteConnectCommand $command, callable $stream) { list($privateKey, $publicKey) = \Undine\Functions\openssl_generate_rsa_key_pair(); $site = (new Site($command->getUrl(), new SiteState(), $this->getUser(), $privateKey, $publicKey))->setHttpCredentials($command->getHttpCredentials())->setFtpCredentials($command->getFtpCredentials()); $drupalClient = $this->get('undine.drupal_client'); $drupalSession = new Session(new CookieJar(), $command->getHttpCredentials(), $command->getFtpCredentials()); // This promise can be present or not, we create a reference here so we can cancel it if it proves to be unnecessary (ie. we're already connected). $findLoginForm = null; // First asynchronously attempt a handshake... $connectWebsite = $this->oxygenClient->sendAsync($site, new SitePingAction())->then(function ($result) use(&$findLoginForm) { // We got a successful handshake; cancel the form lookup. if ($findLoginForm instanceof Promise && $findLoginForm->getState() === Promise::PENDING) { $findLoginForm->cancel(); } return $result; }); // ...and at the same time check if the login form could be found, because most likely the site won't be connected immediately. if ($command->checkUrl() || $command->hasAdminCredentials()) { $findLoginForm = $drupalClient->findLoginFormAsync($command->getUrl(), $drupalSession); } /** @var Promise $settlePromise */ $settlePromise = \GuzzleHttp\Promise\settle(array_filter([$connectWebsite, $findLoginForm])); // [0] will contain 'connectWebsite' result; while [1] will contain 'findLoginForm' result or will not be set. // Also, result arrays contain two members: 'state' and 'value' (if fulfilled) or 'reason' (if rejected). $stream(new SiteConnectProgress(SiteConnectProgress::INITIATE_OXYGEN_HANDSHAKE)); $result = $settlePromise->wait(); if ($result[0]['state'] === Promise::FULFILLED) { // Site connection was fully successful. $this->persistSite($site); return new SiteConnectResult($site); } $exception = $result[0]['reason']; if (!$exception instanceof ProtocolException) { throw $exception; } if ($exception->is(ResponseException::RESPONSE_NOT_FOUND)) { if (!$findLoginForm) { throw new ConstraintViolationException(new E\SiteConnect\OxygenNotFound(false, false)); } // The Oxygen module is not enabled and we did look for a login form. if ($result[1]['state'] === Promise::FULFILLED) { // We got a login form! /** @var Form $form */ $form = $result[1]['value']; if (!$command->hasAdminCredentials()) { throw new ConstraintViolationException(new E\SiteConnect\OxygenNotFound(true, true)); } // We got admin credentials provided; log in and install the Oxygen module. try { $stream(new SiteConnectProgress(SiteConnectProgress::LOG_IN_AS_ADMINISTRATOR)); $drupalClient->login($form, $command->getAdminCredentials()->getUsername(), $command->getAdminCredentials()->getPassword(), $drupalSession); } catch (DCE\InvalidCredentialsException $e) { throw new ConstraintViolationException(new E\DrupalClient\InvalidCredentials()); } try { $stream(new SiteConnectProgress(SiteConnectProgress::LIST_AVAILABLE_MODULES)); $modulesForm = $drupalClient->getModulesForm($command->getUrl(), $drupalSession); } catch (DCE\ModulesFormNotFoundException $e) { throw new ConstraintViolationException(new E\DrupalClient\CanNotInstallOxygen(E\DrupalClient\CanNotInstallOxygen::STEP_LIST_MODULES)); } $moduleList = ModuleList::createFromForm($modulesForm); $updateModule = $moduleList->find('update', 'Core'); if ($updateModule === null) { throw new ConstraintViolationException(new E\DrupalClient\CanNotInstallOxygen(E\DrupalClient\CanNotInstallOxygen::STEP_SEARCH_UPDATE_MODULE)); } if (!$updateModule->isEnabled()) { $stream(new SiteConnectProgress(SiteConnectProgress::ENABLE_UPDATE_MODULE)); $drupalClient->enableModule($modulesForm, $updateModule->getPackage(), $updateModule->getSlug(), $drupalSession); } $oxygenModule = $moduleList->find('oxygen'); if ($oxygenModule === null) { // Oxygen module is not installed; install it now. try { $stream(new SiteConnectProgress(SiteConnectProgress::INSTALL_OXYGEN_MODULE)); $drupalClient->installExtensionFromUrl($command->getUrl(), $this->getParameter('oxygen_zip_url'), $drupalSession); } catch (DCE\FtpCredentialsRequiredException $e) { throw new ConstraintViolationException(new E\Ftp\CredentialsRequired()); } catch (DCE\FtpCredentialsErrorException $e) { throw new ConstraintViolationException(new E\Ftp\CredentialsError($e->getClientMessage())); } $stream(new SiteConnectProgress(SiteConnectProgress::VERIFY_OXYGEN_MODULE_INSTALLED)); $modulesForm = $drupalClient->getModulesForm($command->getUrl(), $drupalSession); $newModuleList = ModuleList::createFromForm($modulesForm); $oxygenModule = $newModuleList->find('oxygen'); if ($oxygenModule === null) { throw new ConstraintViolationException(new E\DrupalClient\CanNotInstallOxygen(E\DrupalClient\CanNotInstallOxygen::STEP_SEARCH_OXYGEN_MODULE)); } } if (!$oxygenModule->isEnabled()) { $stream(new SiteConnectProgress(SiteConnectProgress::ENABLE_OXYGEN_MODULE)); $drupalClient->enableModule($modulesForm, $oxygenModule->getPackage(), $oxygenModule->getSlug(), $drupalSession); } try { // The module might have already been connected to an account - clear its key. $stream(new SiteConnectProgress(SiteConnectProgress::DISCONNECT_OXYGEN_MODULE)); $drupalClient->disconnectOxygen($command->getUrl(), $drupalSession); } catch (DCE\OxygenPageNotFoundException $e) { throw new ConstraintViolationException(new E\DrupalClient\OxygenPageNotFound()); } // @todo: Make sure the module is at the latest version. try { $stream(new SiteConnectProgress(SiteConnectProgress::INITIATE_OXYGEN_HANDSHAKE)); $this->oxygenClient->send($site, new SitePingAction()); } catch (RejectionException $e) { // $e->get } // Site connection was fully successful. $this->persistSite($site); return new SiteConnectResult($site); } elseif ($result[1]['reason'] instanceof DCE\LoginFormNotFoundException) { // No login form found, is this even a Drupal website? throw new ConstraintViolationException(new E\SiteConnect\OxygenNotFound(true, false)); } } elseif ($exception->is(OxygenException::HANDSHAKE_VERIFY_FAILED)) { // The site is connected to another dashboard, but we can reclaim it here if the admin credentials are provided. /* @var ResponseException $oxygenException */ if ($findLoginForm === null) { // We did not look for a login form. throw new ConstraintViolationException(new E\SiteConnect\AlreadyConnected(false, false)); } // We looked for the login form. if ($result[1]['state'] === Promise::FULFILLED) { // We have a login form. /* @var Form $form */ $loginForm = $result[1]['value']; if (!$command->hasAdminCredentials()) { throw new ConstraintViolationException(new E\SiteConnect\AlreadyConnected(true, true)); } try { $stream(new SiteConnectProgress(SiteConnectProgress::LOG_IN_AS_ADMINISTRATOR)); $drupalClient->login($loginForm, $command->getAdminCredentials()->getUsername(), $command->getAdminCredentials()->getPassword(), $drupalSession); } catch (DCE\InvalidCredentialsException $e) { throw new ConstraintViolationException(new E\DrupalClient\InvalidCredentials()); } try { $stream(new SiteConnectProgress(SiteConnectProgress::DISCONNECT_OXYGEN_MODULE)); $drupalClient->disconnectOxygen($command->getUrl(), $drupalSession); } catch (DCE\OxygenPageNotFoundException $e) { throw new ConstraintViolationException(new E\DrupalClient\OxygenPageNotFound()); } // @todo: Make sure the module is at the latest version. $stream(new SiteConnectProgress(SiteConnectProgress::INITIATE_OXYGEN_HANDSHAKE)); $this->oxygenClient->send($site, new SitePingAction()); // Site connection was fully successful. $this->persistSite($site); return new SiteConnectResult($site); } // We did not find a login form. throw new ConstraintViolationException(new E\SiteConnect\AlreadyConnected(true, false)); } throw $result[0]['reason']; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $children = (string) $input->getOption('children'); $this->maxAge = (string) $input->getOption('max-age'); if ((int) $children < 0 || !ctype_digit($children)) { throw new \InvalidArgumentException('Number of children must be greater than 0.'); } if (strtotime($this->maxAge) === false) { throw new \InvalidArgumentException(sprintf('The age "%s" is not valid.', $this->maxAge)); } if (strtotime($this->maxAge) >= time()) { throw new \InvalidArgumentException(sprintf('The age "%s" is not set in the past.', $this->maxAge)); } $children = (int) $children; $promises = []; for ($i = 0; $i < $children; ++$i) { // Delay a few ms, to reduce chance of race condition. $promises[] = $this->loop([$this, 'fetchThumbnail'], [CallbackOptions::DELAY => random_int(10, 200)]); } $this->logger->info('Initiated asynchronous thumbnail capture daemons.', ['childCount' => $children]); \GuzzleHttp\Promise\settle($promises)->wait(); }