diff --git a/app/code/community/Adyen/Payment/Model/Observer.php b/app/code/community/Adyen/Payment/Model/Observer.php index 446cdb56..d1087631 100644 --- a/app/code/community/Adyen/Payment/Model/Observer.php +++ b/app/code/community/Adyen/Payment/Model/Observer.php @@ -82,6 +82,7 @@ public function addMethodsToConfig(Varien_Event_Observer $observer = null) } } + if (Mage::getStoreConfigFlag('payment/adyen_hpp/active', $store)) { // by default disable adyen_ideal only if IDeal is in directoryLookup result show this payment method $store->setConfig('payment/adyen_ideal/active', 0); diff --git a/app/code/community/Ebanx/Gateway/Block/Adminhtml/System/Config/Form/Field/Interest.php b/app/code/community/Ebanx/Gateway/Block/Adminhtml/System/Config/Form/Field/Interest.php new file mode 100644 index 00000000..bf16eec5 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Adminhtml/System/Config/Form/Field/Interest.php @@ -0,0 +1,80 @@ +addColumn('instalments', array( + 'label' => Mage::helper('ebanx')->__('Up To'), + 'style' => 'width:120px', + )); + + $this->addColumn('interest', array( + 'label' => Mage::helper('ebanx')->__('Interest Rate'), + 'style' => 'width:120px', + )); + + $this->_addAfter = false; + + parent::__construct(); + } + + /** + * Render array cell for prototypeJS template + * + * @param string $columnName + * @return string + */ + protected function _renderCellTemplate($columnName) + { + if (empty($this->_columns[$columnName])) { + throw new Exception('Wrong column name specified.'); + } + + $column = $this->_columns[$columnName]; + $elementName = $this->getElement()->getName() . '[#{_id}][' . $columnName . ']'; + $extraParams = ' data-value="#{' . $columnName . '}" ' . + (isset($column['style']) ? ' style="' . $column['style'] . '"' : ''); + + if ($columnName == 'instalments') { + return $this->getInstallmentsSelectHtml($elementName, $extraParams); + } + return parent::_renderCellTemplate($columnName); + } + + /** + * @param $name + * @param $extraParams + * @return string + */ + public function getInstallmentsSelectHtml($name, $extraParams) + { + $select = $this->getLayout()->createBlock('adminhtml/html_select') + ->setName($name) + ->setClass('select-instalments') + ->setExtraParams($extraParams) + ->setOptions(Mage::getSingleton('ebanx/source_instalment')->toOptionArray()); + + return $select->getHtml(); + } + + /** + * Render block HTML + * + * @return string + */ + protected function _toHtml() + { + $fieldId = $this->getElement()->getId(); + + $html = "
"; + $html .= parent::_toHtml(); + $html .= Mage::helper('adminhtml/js')->getScript( + "$$('.select-instalments').each(function(el){ el.value = el.readAttribute('data-value'); });\n" + ); + $html .= '
'; + + return $html; + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Catalog/Product/View/Oneclick.php b/app/code/community/Ebanx/Gateway/Block/Catalog/Product/View/Oneclick.php new file mode 100644 index 00000000..716faff7 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Catalog/Product/View/Oneclick.php @@ -0,0 +1,173 @@ +initialize(); + } + + public function getText() + { + $country = $this->getCountry(); + $text = array( + 'local-amount' => 'Total a pagar en Peso mexicano: ', + 'cvv' => 'Código de verificación', + 'instalments' => 'Número de parcelas', + ); + switch ($country) { + case 'BR': + $text['local-amount'] = $this->getLocalAmountText(); + $text['cvv'] = 'Código de segurança'; + break; + case 'CO': + $text['local-amount'] = 'Total a pagar en Peso mexicano: '; + break; + case 'AR': + $text['local-amount'] = 'Total a pagar en Peso argentino: '; + break; + default: + break; + } + return $text; + } + + public function getAddress() + { + $addressId = $this->customer->getDefaultShipping(); + if (!$addressId) { + return array(); + } + $address = Mage::getModel('customer/address')->load($addressId)->getData(); + + return $address; + } + + public function canShowOneclickButton() + { + return Mage::getSingleton('customer/session')->isLoggedIn() + && Mage::getStoreConfig('payment/ebanx_settings/one_click_payment') + && $this->usercards + && $this->usercards->getSize() + && $this->getAddress()['street'] + && ($this->customer->getEbanxCustomerDocument() + || $this->countryDocumentIsOptional($this->getCountry())); + } + + private function initialize() + { + if (!Mage::getSingleton('customer/session')->isLoggedIn()) { + $this->usercards = array(); + } + $this->customer = Mage::getSingleton('customer/session')->getCustomer(); + + $this->usercards = Mage::getModel('ebanx/usercard')->getCustomerSavedCards($this->customer->getId()); + } + + private function countryDocumentIsOptional($country) { + $isOptional = array( + 'MX', + 'CO', + 'AR', + ); + + return in_array($country, $isOptional); + } + + /** + * @return string + */ + private function getCountry() + { + $address = $this->getAddress(); + if (!array_key_exists('country_id', $address)) { + return ''; + } + + return $address['country_id']; + } + + /** + * @return string + */ + public function getLocalCurrency() + { + switch ($this->getCountry()) { + case 'MX': + return 'MXN'; + case 'CO': + return 'COP'; + case 'AR': + return 'ARS'; + case 'BR': + default: + return 'BRL'; + } + } + + public function getLocalAmount($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithTax($currency, $this->getTotal()), 2); + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } + + private function formatPriceWithLocalCurrency($currency, $price) + { + return Mage::app()->getLocale()->currency($currency)->toCurrency($price); + } + + public function getTotal() + { + return Mage::registry('current_product')->getPrice(); + } + + public function getInstalmentTerms() + { + return $this->getMethod()->getInstalmentTerms($this->getTotal()); + } + + /** + * @return Ebanx_Gateway_Model_Payment_Creditcard + */ + private function getMethod() + { + switch ($this->getCountry()) { + case 'MX': + return new Ebanx_Gateway_Model_Mexico_Creditcard(); + case 'CO': + return new Ebanx_Gateway_Model_Colombia_Creditcard(); + case 'AR': + return new Ebanx_Gateway_Model_Argentina_Creditcard(); + case 'BR': + default: + return new Ebanx_Gateway_Model_Brazil_Creditcard(); + } + } + + private function getLocalAmountText() { + return Mage::getStoreConfig('payment/ebanx_settings/iof_local_amount') + ? 'Total a pagar com IOF (0.38%): ' + : 'Total a pagar: '; + } + + public function formatInstalment($instalment, $localCurrency) + { + $amount = Mage::app()->getLocale()->currency($localCurrency)->toCurrency($instalment->localAmountWithTax); + $instalmentNumber = $instalment->instalmentNumber; + $interestMessage = $this->getInterestMessage($instalment->hasInterests); + $message = sprintf('%sx de %s %s', $instalmentNumber, $amount, $interestMessage); + + return $message; + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Checkout/Cart/Total.php b/app/code/community/Ebanx/Gateway/Block/Checkout/Cart/Total.php new file mode 100644 index 00000000..8e6bce8b --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Checkout/Cart/Total.php @@ -0,0 +1,41 @@ +setTemplate('ebanx/checkout/cart/total.phtml'); + } + + protected function _toHtml() + { + if ($this->getAmount() == $this->_getQuote()->getBaseGrandTotal()) { + return ''; + } + return parent::_toHtml(); + } + + public function getAmount() + { + $amount = $this->_getQuote()->getBaseGrandTotal(); + $payment = $this->_getPayment(); + if ($payment->getMethod() == 'ebanx_cc_br') { +// TODO: Get instalments +// TODO: Get interest rate +// TODO: Calc amount + } + + return $amount; + } + + protected function _getQuote() + { + return Mage::getSingleton('checkout/session')->getQuote(); + } + + protected function _getPayment() + { + return $this->_getQuote()->getPayment(); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Cashpayment.php b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Cashpayment.php new file mode 100644 index 00000000..9a382b72 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Cashpayment.php @@ -0,0 +1,54 @@ +getPayment()->getEbanxDueDate()); + + return $date->get($format); + } + + public function getEbanxUrlPrint() + { + $hash = $this->getEbanxPaymentHash(); + return $this->helper->getVoucherUrlByHash($hash, 'print'); + } + + public function getEbanxPaymentHash() + { + return $this->getOrder()->getPayment()->getEbanxPaymentHash(); + } + + public function getEbanxUrlPdf() + { + $hash = $this->getEbanxPaymentHash(); + return $this->helper->getVoucherUrlByHash($hash, 'pdf'); + } + + public function getEbanxUrlBasic() + { + $hash = $this->getEbanxPaymentHash(); + return $this->helper->getVoucherUrlByHash($hash, 'basic'); + } + + public function getVoucherUrl() + { + return Mage::getUrl('ebanx/voucher', array( + 'hash' => $this->getEbanxPaymentHash() + )); + } + + public function getEbanxUrlMobile() + { + $hash = $this->getEbanxPaymentHash(); + return $this->helper->getVoucherUrlByHash($hash, 'mobile'); + } + + protected function _construct() + { + parent::_construct(); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Creditcardpayment.php b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Creditcardpayment.php new file mode 100644 index 00000000..3d287f29 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Creditcardpayment.php @@ -0,0 +1,10 @@ +helper = Mage::helper('ebanx/order'); + } + + public function getSuccessPaymentBlock() + { + return $this->getPayment()->getMethodInstance()->getCode(); + } + + public function getPayment() + { + return $this->getOrder()->getPayment(); + } + + public function getOrder() + { + if (is_null($this->_order)) { + $lastOrderId = Mage::getSingleton('checkout/session')->getLastOrderId(); + $this->_order = Mage::getModel('sales/order')->load($lastOrderId); + } + + return $this->_order; + } + + public function getCustomer() + { + $customerId = $this->getOrder()->getCustomerId(); + + return array_merge( + $this->helper->getCustomerData(), + Mage::getModel('customer/customer')->load($customerId)->getData() + ); + } + + public function formatPriceWithLocalCurrency($currency, $price) + { + return Mage::app()->getLocale()->currency($currency)->toCurrency($price); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Payment/Baloto.php b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Payment/Baloto.php new file mode 100644 index 00000000..b432aeb9 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Payment/Baloto.php @@ -0,0 +1,14 @@ +getEbanxBarCode(); + + return array( + 'boleto1' => '' . substr($code, 0, 5) . '', + 'boleto2' => '' . substr($code, 5, 5) . '', + 'boleto3' => '' . substr($code, 10, 5) . '', + 'boleto4' => '' . substr($code, 15, 6) . '', + 'boleto5' => '' . substr($code, 21, 5) . '', + 'boleto6' => '' . substr($code, 26, 6) . '', + 'boleto7' => '' . substr($code, 32, 1) . '', + 'boleto8' => '' . substr($code, 33, 14) . '', + ); + } + + public function getEbanxBarCode() + { + return $this->getOrder()->getPayment()->getEbanxBarCode(); + } + + protected function _construct() + { + parent::_construct(); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Payment/Creditcardar.php b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Payment/Creditcardar.php new file mode 100644 index 00000000..dd0b53d1 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Checkout/Success/Payment/Creditcardar.php @@ -0,0 +1,11 @@ +getCustomer(); + } + + /** + * Wrapper for getItems() method + * + * @return array + */ + public function getCards() + { + if (is_null($this->cards)) { + $this->cards = $this->getItems(); + } + + return $this->cards; + } + + /** + * Constructor. Prepare user cards list + * + * @return Mage_Core_Block_Template + */ + public function __construct() + { + parent::__construct(); + + $items = Mage::getResourceModel('ebanx/usercard_collection') + ->addFieldToFilter('user_id', $this->getCustomer()->getId()) + ->addOrder('ebanx_card_id', 'desc'); + + $this->setItems($items); + + return $this; + } + + /** + * Prepare layout + * + * @return Mage_Core_Block_Template + */ + protected function _prepareLayout() + { + parent::_prepareLayout(); + + $pager = $this->getLayout()->createBlock('page/html_pager', 'ebanx.customer.cards.pager') + ->setCollection($this->getItems()); + + $this->setChild('pager', $pager); + + $this->getItems()->load(); + + return $this; + } + + /** + * Get form action URL + * + * @return string + */ + public function getFormUrl() + { + return Mage::getUrl('ebanx/customer/removecard'); + } + + /** + * Get "Back" URL + * + * @return string + */ + public function getBackUrl() + { + if ($this->getRefererUrl()) { + $url = $this->getRefererUrl(); + } else { + $url = Mage::getUrl('customer/account/'); + } + + return $url; + } + + /** + * Get Add new card URL + * + * @return string + */ + public function getAddCardUrl() + { + return Mage::getUrl('ebanx/customer/cardadd'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Abstract.php b/app/code/community/Ebanx/Gateway/Block/Form/Abstract.php new file mode 100644 index 00000000..0eb6b046 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Abstract.php @@ -0,0 +1,28 @@ +getMethod()->getTotal(); + } + + private function formatPriceWithLocalCurrency($currency, $price) + { + return Mage::app()->getLocale()->currency($currency)->toCurrency($price); + } + + public function getLocalAmount($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithTax($currency, $this->getTotal()), 2); + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } + + public function getLocalAmountWithoutTax($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithoutTax($currency, $this->getTotal()), 2); + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Baloto.php b/app/code/community/Ebanx/Gateway/Block/Form/Baloto.php new file mode 100644 index 00000000..568b2da3 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Baloto.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/baloto.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Boleto.php b/app/code/community/Ebanx/Gateway/Block/Form/Boleto.php new file mode 100644 index 00000000..6724f560 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Boleto.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/boleto.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Creditcard.php b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard.php new file mode 100644 index 00000000..db4ff6bf --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard.php @@ -0,0 +1,77 @@ +getMethod()->getInstalmentTerms(); + } + + public function getTotal() + { + return $this->getMethod()->getTotal(); + } + + public function canShowSaveCardOption() + { + return Mage::getSingleton('checkout/session')->getQuote()->getCheckoutMethod() == "register" || Mage::getSingleton('customer/session')->isLoggedIn(); + } + + /** + * @return array + */ + protected function getSavedCards() + { + if (!Mage::getSingleton('customer/session')->isLoggedIn()) { + return array(); + } + $customerId = Mage::getSingleton('customer/session')->getCustomer()->getId(); + + return Mage::getModel('ebanx/usercard')->getCustomerSavedCards($customerId); + } + + private function formatPriceWithLocalCurrency($currency, $price) + { + return Mage::app()->getLocale()->currency($currency)->toCurrency($price); + } + + public function getLocalAmount($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithTax($currency, $this->getTotal()), 2); + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } + + public function getLocalAmountWithoutTax($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithoutTax($currency, $this->getTotal()), 2); + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } + + public function formatInstalment($instalment, $localCurrency) + { + $amount = Mage::app()->getLocale()->currency($localCurrency)->toCurrency($instalment->localAmountWithTax); + $instalmentNumber = $instalment->instalmentNumber; + $interestMessage = $this->getInterestMessage($instalment->hasInterests); + $message = sprintf('%sx de %s %s', $instalmentNumber, $amount, $interestMessage); + return $message; + } + + /** + * @param bool $hasInterests + * @return string + */ + abstract protected function getInterestMessage($hasInterests); + + protected function _construct() + { + parent::_construct(); + $this->setTemplate($this->getTemplatePath()); + } + + /** + * @return string + */ + abstract protected function getTemplatePath(); +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Ar.php b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Ar.php new file mode 100644 index 00000000..cd33a00d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Ar.php @@ -0,0 +1,39 @@ + 'Pagar con Tarjeta de Crédito.', + 'newcard' => 'Nueva tarjeta', + 'local-amount' => 'Total a pagar en Peso argentino: ', + 'card-number' => 'Número de la tarjeta', + 'duedate' => 'Fecha de expiración', + 'cvv' => 'Código de verificación', + 'save' => 'Salvar este cartão para compras futuras', + 'instalments' => 'Número de parcelas', + 'name' => 'Titular de la tarjeta', + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Br.php b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Br.php new file mode 100644 index 00000000..25f9d36f --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Br.php @@ -0,0 +1,45 @@ + 'Pagar com Cartão de Crédito.', + 'newcard' => 'Novo cartão', + 'local-amount' => $this->getLocalAmountText(), + 'card-number' => 'Número do Cartão', + 'duedate' => 'Data de validade', + 'cvv' => 'Código de segurança', + 'save' => 'Salvar este cartão para compras futuras', + 'instalments' => 'Número de parcelas', + 'name' => '', + ); + } + + private function getLocalAmountText() { + return Mage::getStoreConfig('payment/ebanx_settings/iof_local_amount') + ? 'Total a pagar com IOF (0.38%): ' + : 'Total a pagar: '; + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Co.php b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Co.php new file mode 100644 index 00000000..3a049eea --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Co.php @@ -0,0 +1,39 @@ + 'Pagar con Tarjeta de Crédito.', + 'newcard' => 'Nueva tarjeta', + 'local-amount' => 'Total a pagar en Peso colombiano: ', + 'card-number' => 'Número de la tarjeta', + 'duedate' => 'Fecha de expiración', + 'cvv' => 'Código de verificación', + 'save' => 'Salvar este cartão para compras futuras', + 'instalments' => 'Número de parcelas', + 'name' => 'Titular de la tarjeta', + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Mx.php b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Mx.php new file mode 100644 index 00000000..e6b53e6b --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Creditcard/Mx.php @@ -0,0 +1,39 @@ + 'Pagar con Tarjeta de Crédito.', + 'newcard' => 'Nueva tarjeta', + 'local-amount' => 'Total a pagar en Peso mexicano: ', + 'card-number' => 'Número de la tarjeta', + 'duedate' => 'Fecha de expiración', + 'cvv' => 'Código de verificación', + 'save' => 'Salvar este cartão para compras futuras', + 'instalments' => 'Número de parcelas', + 'name' => 'Titular de la tarjeta', + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Debitcard.php b/app/code/community/Ebanx/Gateway/Block/Form/Debitcard.php new file mode 100644 index 00000000..8561e5ff --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Debitcard.php @@ -0,0 +1,29 @@ +setTemplate('ebanx/form/debitcard.phtml'); + } + + private function formatPriceWithLocalCurrency($currency, $price) + { + return Mage::app()->getLocale()->currency($currency)->toCurrency($price); + } + + public function getLocalAmount($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithTax($currency, $this->getMethod()->getTotal()), 2); + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } + + public function getLocalAmountWithoutTax($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithoutTax($currency, $this->getMethod()->getTotal()), 2); + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Multicaja.php b/app/code/community/Ebanx/Gateway/Block/Form/Multicaja.php new file mode 100644 index 00000000..f6e454fb --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Multicaja.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/multicaja.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Otroscupones.php b/app/code/community/Ebanx/Gateway/Block/Form/Otroscupones.php new file mode 100644 index 00000000..7497fe2b --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Otroscupones.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/otroscupones.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Oxxo.php b/app/code/community/Ebanx/Gateway/Block/Form/Oxxo.php new file mode 100644 index 00000000..7c2fb132 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Oxxo.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/oxxo.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Pagoefectivo.php b/app/code/community/Ebanx/Gateway/Block/Form/Pagoefectivo.php new file mode 100644 index 00000000..4d36dddd --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Pagoefectivo.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/pagoefectivo.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Pagofacil.php b/app/code/community/Ebanx/Gateway/Block/Form/Pagofacil.php new file mode 100644 index 00000000..f03aade3 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Pagofacil.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/pagofacil.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Pse.php b/app/code/community/Ebanx/Gateway/Block/Form/Pse.php new file mode 100644 index 00000000..f747ff39 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Pse.php @@ -0,0 +1,35 @@ + 'Banco Agrario', + 'banco_av_villas' => 'Banco AV Villas', + 'banco_bbva_colombia_s.a.' => 'Banco BBVA Colombia', + 'banco_caja_social' => 'Banco Caja Social', + 'banco_colpatria' => 'Banco Colpatria', + 'banco_cooperativo_coopcentral' => 'Banco Cooperativo Coopcentral', + 'banco_corpbanca_s.a' => 'Banco CorpBanca Colombia', + 'banco_davivienda' => 'Banco Davivienda', + 'banco_de_bogota' => 'Banco de Bogotá', + 'banco_de_occidente' => 'Banco de Occidente', + 'banco_falabella_' => 'Banco Falabella', + 'banco_gnb_sudameris' => 'Banco GNB Sudameris', + 'banco_pichincha_s.a.' => 'Banco Pichincha', + 'banco_popular' => 'Banco Popular', + 'banco_procredit' => 'Banco ProCredit', + 'bancolombia' => 'Bancolombia', + 'bancoomeva_s.a.' => 'Bancoomeva', + 'citibank_' => 'Citibank', + 'helm_bank_s.a.' => 'Helm Bank' + ); + } + + protected function _construct() + { + parent::_construct(); + $this->setTemplate('ebanx/form/pse.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Rapipago.php b/app/code/community/Ebanx/Gateway/Block/Form/Rapipago.php new file mode 100644 index 00000000..15a446dd --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Rapipago.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/rapipago.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Safetypay/Ec.php b/app/code/community/Ebanx/Gateway/Block/Form/Safetypay/Ec.php new file mode 100644 index 00000000..a44842f7 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Safetypay/Ec.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/safetypayec.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Safetypay/Pe.php b/app/code/community/Ebanx/Gateway/Block/Form/Safetypay/Pe.php new file mode 100644 index 00000000..b631a6d6 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Safetypay/Pe.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/safetypaype.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Sencillito.php b/app/code/community/Ebanx/Gateway/Block/Form/Sencillito.php new file mode 100644 index 00000000..0661c31f --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Sencillito.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/sencillito.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Servipag.php b/app/code/community/Ebanx/Gateway/Block/Form/Servipag.php new file mode 100644 index 00000000..c05148e1 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Servipag.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/servipag.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Spei.php b/app/code/community/Ebanx/Gateway/Block/Form/Spei.php new file mode 100644 index 00000000..abd3865e --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Spei.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/spei.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Tef.php b/app/code/community/Ebanx/Gateway/Block/Form/Tef.php new file mode 100644 index 00000000..d0ee572f --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Tef.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/tef.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Wallet.php b/app/code/community/Ebanx/Gateway/Block/Form/Wallet.php new file mode 100644 index 00000000..43a128c4 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Wallet.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/wallet.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Form/Webpay.php b/app/code/community/Ebanx/Gateway/Block/Form/Webpay.php new file mode 100644 index 00000000..688a2e32 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Form/Webpay.php @@ -0,0 +1,10 @@ +setTemplate('ebanx/form/webpay.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Abstract.php b/app/code/community/Ebanx/Gateway/Block/Info/Abstract.php new file mode 100644 index 00000000..dc27634c --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Abstract.php @@ -0,0 +1,68 @@ +getMethod()->getTotal(); + } + + private function formatPriceWithLocalCurrency($currency, $price) + { + return Mage::app()->getLocale()->currency($currency)->toCurrency($price); + } + + public function getLocalAmount($currency, $formatted = true) + { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithTax($currency, $this->getTotal()), 2); + + if ($this->shouldntShowIof()) { + $amount = round(Mage::helper('ebanx')->getLocalAmountWithoutTax($currency, $this->getTotal()), 2); + } + + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $amount) : $amount; + } + + public function getLocalAmountWithoutTax($currency, $formatted = true){ + return $formatted ? $this->formatPriceWithLocalCurrency($currency, $this->getTotal()) : $this->getTotal(); + } + + public function shouldntShowIof() { + return Mage::getStoreConfig('payment/ebanx_settings/iof_local_amount') === '0'; + } + + protected function isAdmin() { + if(Mage::app()->getStore()->isAdmin()) + { + return true; + } + + if(Mage::getDesign()->getArea() == 'adminhtml') + { + return true; + } + + return false; + } + + protected function getDashboardUrl($hash) { + return sprintf( + 'https://dashboard.ebanx.com%s/payments/?hash=%s', + $this->getInfo()->getEbanxEnvironment() === 'sandbox' + ? '/test' + : '', + $hash + ); + } + + protected function getNotificationUrl($hash) { + return $this->getUrl( + 'ebanx/payment/notify', + array( + 'hash_codes' => $hash, + 'operation' => 'update', + 'notification_type' => 'forced', + ) + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Baloto.php b/app/code/community/Ebanx/Gateway/Block/Info/Baloto.php new file mode 100644 index 00000000..3d7c1c5e --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Baloto.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/baloto.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Boleto.php b/app/code/community/Ebanx/Gateway/Block/Info/Boleto.php new file mode 100644 index 00000000..eab99b7d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Boleto.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/boleto.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Creditcardar.php b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardar.php new file mode 100644 index 00000000..cc004569 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardar.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/creditcard_ar.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Creditcardbr.php b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardbr.php new file mode 100644 index 00000000..135f9464 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardbr.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/creditcard_br.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Creditcardco.php b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardco.php new file mode 100644 index 00000000..ab2a13aa --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardco.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/creditcard_co.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Creditcardmx.php b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardmx.php new file mode 100644 index 00000000..e0dcc6c3 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Creditcardmx.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/creditcard_mx.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Debitcard.php b/app/code/community/Ebanx/Gateway/Block/Info/Debitcard.php new file mode 100644 index 00000000..b5e0cb89 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Debitcard.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/debitcard.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Legacy.php b/app/code/community/Ebanx/Gateway/Block/Info/Legacy.php new file mode 100644 index 00000000..ea24c12a --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Legacy.php @@ -0,0 +1,15 @@ +isAdmin()) { + return; + } + + $this->setTemplate('ebanx/info/default.phtml'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Multicaja.php b/app/code/community/Ebanx/Gateway/Block/Info/Multicaja.php new file mode 100644 index 00000000..48863a3b --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Multicaja.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/multicaja.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Otroscupones.php b/app/code/community/Ebanx/Gateway/Block/Info/Otroscupones.php new file mode 100644 index 00000000..a079311e --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Otroscupones.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/otroscupones.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Oxxo.php b/app/code/community/Ebanx/Gateway/Block/Info/Oxxo.php new file mode 100644 index 00000000..30325dc3 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Oxxo.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/oxxo.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Pagoefectivo.php b/app/code/community/Ebanx/Gateway/Block/Info/Pagoefectivo.php new file mode 100644 index 00000000..85be5aea --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Pagoefectivo.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/pagoefectivo.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Pagofacil.php b/app/code/community/Ebanx/Gateway/Block/Info/Pagofacil.php new file mode 100644 index 00000000..44343091 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Pagofacil.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/pagofacil.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Pse.php b/app/code/community/Ebanx/Gateway/Block/Info/Pse.php new file mode 100644 index 00000000..1c7f1419 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Pse.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/pse.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Rapipago.php b/app/code/community/Ebanx/Gateway/Block/Info/Rapipago.php new file mode 100644 index 00000000..03387c61 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Rapipago.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/rapipago.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Safetypayec.php b/app/code/community/Ebanx/Gateway/Block/Info/Safetypayec.php new file mode 100644 index 00000000..b3e9b62b --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Safetypayec.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/safetypayec.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Safetypaype.php b/app/code/community/Ebanx/Gateway/Block/Info/Safetypaype.php new file mode 100644 index 00000000..139685bc --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Safetypaype.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/safetypaype.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Sencillito.php b/app/code/community/Ebanx/Gateway/Block/Info/Sencillito.php new file mode 100644 index 00000000..97f98759 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Sencillito.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/sencillito.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Servipag.php b/app/code/community/Ebanx/Gateway/Block/Info/Servipag.php new file mode 100644 index 00000000..58072984 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Servipag.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/servipag.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Spei.php b/app/code/community/Ebanx/Gateway/Block/Info/Spei.php new file mode 100644 index 00000000..d755d482 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Spei.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/spei.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Tef.php b/app/code/community/Ebanx/Gateway/Block/Info/Tef.php new file mode 100644 index 00000000..9c8c0a79 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Tef.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/tef.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Wallet.php b/app/code/community/Ebanx/Gateway/Block/Info/Wallet.php new file mode 100644 index 00000000..84c629c1 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Wallet.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/wallet.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Block/Info/Webpay.php b/app/code/community/Ebanx/Gateway/Block/Info/Webpay.php new file mode 100644 index 00000000..33559e9e --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Block/Info/Webpay.php @@ -0,0 +1,13 @@ +setTemplate('ebanx/info/webpay.phtml'); + if ($this->isAdmin()) { + $this->setTemplate('ebanx/info/default.phtml'); + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Exception.php b/app/code/community/Ebanx/Gateway/Exception.php new file mode 100644 index 00000000..e35b2f2f --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Exception.php @@ -0,0 +1,6 @@ +isSandboxMode() ? self::URL_PRINT_SANDBOX : self::URL_PRINT_LIVE; + } + + public function isSandboxMode() + { + return $this->getMode() === Ebanx_Gateway_Model_Source_Mode::SANDBOX; + } + + public function getMode() + { + return Mage::getStoreConfig('payment/ebanx_settings/mode'); + } + + public function getSandboxIntegrationKey() + { + return Mage::getStoreConfig('payment/ebanx_settings/integration_key_' . Ebanx_Gateway_Model_Source_Mode::SANDBOX); + } + + public function getLiveIntegrationKey() + { + return Mage::getStoreConfig('payment/ebanx_settings/integration_key_' . Ebanx_Gateway_Model_Source_Mode::LIVE); + } + + public function areKeysFilled() + { + $keys = $this->getIntegrationKey(); + $publicKeys = $this->getPublicIntegrationKey(); + return !empty($keys) && !empty($publicKeys); + } + + public function getIntegrationKey() + { + return Mage::getStoreConfig('payment/ebanx_settings/integration_key_' . $this->getMode()); + } + + public function getPublicIntegrationKey() + { + return Mage::getStoreConfig('payment/ebanx_settings/integration_key_public_' . $this->getMode()); + } + + public function getDueDate($date = null, $format = 'YYYY-MM-dd HH:mm:ss') + { + $date = !is_null($date) ? $date : Mage::getModel('core/date')->timestamp(); + $dueDate = new Zend_Date($date); + + return $dueDate->addDay($this->getDueDateDays())->get($format); + } + + public function getDueDateDays() + { + return Mage::getStoreConfig('payment/ebanx_settings/due_date_days'); + } + + public function getMaxInstalments() + { + return Mage::getStoreConfig('payment/ebanx_settings/max_instalments'); + } + + public function getMinInstalmentValue() + { + return Mage::getStoreConfig('payment/ebanx_settings/min_instalment_value'); + } + + public function getInterestRate() + { + return Mage::getStoreConfig('payment/ebanx_settings/interest_rate'); + } + + public function saveCreditCardAllowed() + { + return Mage::getStoreConfig('payment/ebanx_settings/save_card_data'); + } + + public function transformTefToBankName($bankCode) + { + $banks = array( + 'itau' => Bank::ITAU, + 'bradesco' => Bank::BRADESCO, + 'bancodobrasil' => Bank::BANCO_DO_BRASIL, + 'banrisul' => Bank::BANRISUL + ); + + return $banks[strtolower($bankCode)]; + } + + public function hasDocumentFieldAlreadyForMethod($methodCode) + { + $fields = $this->getDocumentFieldsRequiredForMethod($methodCode); + + if (empty($fields)) { + return true; + } + + foreach ($fields as $field) { + $documentFieldName = Mage::getStoreConfig('payment/ebanx_settings/' . $field); + if ($documentFieldName) { + if (!Mage::getSingleton('customer/session')->isLoggedIn() + || Mage::getSingleton('checkout/session')->getQuote()->getCheckoutMethod() === 'register') { + return true; + } + + $customerId = Mage::getSingleton('customer/session')->getCustomer()->getId(); + $customer = Mage::getModel('customer/customer')->load($customerId); + + $customerHasSavedAddress = $customer->getDefaultShipping(); + $customerHasSavedDocument = $customer->getData($documentFieldName); + if ($customerHasSavedAddress && $customerHasSavedDocument) { + return true; + } + } + } + + return false; + } + + public function getLabelForComplianceField($code) + { + switch ($code) { + case 'ebanx_boleto': + case 'ebanx_tef': + case 'ebanx_wallet': + case 'ebanx_cc_br': + return $this->getBrazilianDocumentLabel(); + + case 'ebanx_webpay': + return $this->__('RUT Document'); + + case 'ebanx_pagoefectivo': + case 'ebanx_safetypay': + case 'ebanx_cc_co': + return $this->__('DNI Document'); + + case 'ebanx_pagofacil': + case 'ebanx_rapipago': + case 'ebanx_cupon': + case 'ebanx_cc_ar': + return 'Document'; + + default: + return $this->__('Document Number'); + } + } + + public function getLabelForComplianceFieldByCountry($countryCode) + { + switch (strtolower($countryCode)) { + case 'br': + return $this->getBrazilianDocumentLabel(); + case 'cl': + return $this->__('RUT Document'); + case 'pe': + case 'co': + return $this->__('DNI Document'); + case 'ar': + return 'Documento'; + default: + return $this->__('Document Number'); + } + } + + public function getBrazilianDocumentLabel() + { + $label = array(); + $taxes = explode(',', Mage::getStoreConfig('payment/ebanx_settings/brazil_taxes')); + + return strtoupper(implode(' / ', $taxes)); + } + + public function getDocumentNumber($order, $data) + { + $this->order = $order; + $countryCode = $this->getCustomerData()['country_id']; + $country = $this->transformCountryCodeToName($countryCode); + $methodCode = $data->getEbanxMethod(); + + switch ($country) { + case Country::BRAZIL: + return $this->getBrazilianDocumentNumber($methodCode); + case Country::CHILE: + return $this->getChileanDocumentNumber($methodCode); + case Country::COLOMBIA: + return $this->getColombianDocumentNumber($methodCode); + case Country::ARGENTINA: + return $this->getArgetinianDocument($methodCode); + case Country::PERU: + return $this->getPeruvianDocumentNumber($methodCode); + default: + return null; + } + } + + public function getCustomerData() + { + $checkoutData = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress()->getData(); + + $customerAddressData = array_key_exists('customer_address_id', $checkoutData) && !is_null($checkoutData['customer_address_id']) + ? Mage::getModel('customer/address')->load($checkoutData['customer_address_id'])->getCustomer()->getData() + : $checkoutData; + + $customerSessionData = Mage::getSingleton('customer/session')->getCustomer()->getData(); + + $customerParams = Mage::app()->getRequest()->getParams(); + + $data = array_merge( + $checkoutData, + $customerAddressData, + $customerSessionData, + $customerParams + ); + + return $data; + } + + public function transformCountryCodeToName($countryCode) + { + if (!$countryCode) { + return false; + } + + $country = Country::fromIso($countryCode); + + if (!$country) { + return false; + } + + return $country; + } + + public function getBrazilianDocumentNumber($methodCode) + { + $customer = $this->getCustomerData(); + + if (array_key_exists('ebanx-document', $customer) && isset($customer['ebanx-document'][$methodCode])) { + return $customer['ebanx-document'][$methodCode]; + } + + if ($cpfField = Mage::getStoreConfig('payment/ebanx_settings/cpf_field')) { + if ($cpfField === 'taxvat') { + return $this->order->getCustomerTaxvat(); + } + + if ($customer[$cpfField]) { + return $customer[$cpfField]; + } + } + + if ($cnpjField = Mage::getStoreConfig('payment/ebanx_settings/cnpj_field')) { + if ($cnpjField === 'taxvat') { + return $this->order->getCustomerTaxvat(); + } + + if ($customer[$cnpjField]) { + return $customer[$cnpjField]; + } + } + + + return $customer['ebanx-document'][$methodCode]; + } + + public function getChileanDocumentNumber($methodCode) + { + $customer = $this->getCustomerData(); + + if ($rutField = Mage::getStoreConfig('payment/ebanx_settings/rut_field')) { + if ($rutField === 'taxvat') { + return $this->order->getCustomerTaxvat(); + } + + if ($customer[$rutField]) { + return $customer[$rutField]; + } + } + + if(!is_array($customer) + || !array_key_exists('ebanx-document', $customer) + || !is_array($customer['ebanx-document']) + || !array_key_exists($methodCode, $customer['ebanx-document']) + ) { + return ''; + } + + return $customer['ebanx-document'][$methodCode]; + } + + public function getDocumentType($country) + { + if ($country !== 'AR') { + return null; + } + + $customer = $this->getCustomerData(); + + if ($typeField = Mage::getStoreConfig('payment/ebanx_settings/document_type_field')) { + if ($typeField === 'taxvat') { + return $this->order->getBillingAddress()->getEbanxDocumentType(); + } + + if ($customer[$typeField]) { + return $customer[$typeField]; + } + } + + if(!is_array($customer) + || !array_key_exists('ebanx-document-type', $customer) + || !is_array($customer['ebanx-document-type']) + || !array_key_exists($country, $customer['ebanx-document-type']) + ) { + return ''; + } + + return $customer['ebanx-document-type'][$country]; + } + + public function getColombianDocumentNumber($methodCode) + { + $customer = $this->getCustomerData(); + + if ($dniField = Mage::getStoreConfig('payment/ebanx_settings/dni_field')) { + if ($dniField === 'taxvat') { + return $this->order->getCustomerTaxvat(); + } + + if ($customer[$dniField]) { + return $customer[$dniField]; + } + } + + if(!is_array($customer) + || !array_key_exists('ebanx-document', $customer) + || !is_array($customer['ebanx-document']) + || !array_key_exists($methodCode, $customer['ebanx-document']) + ) { + return ''; + } + + return $customer['ebanx-document'][$methodCode]; + } + + public function getArgetinianDocument($methodCode) + { + $customer = $this->getCustomerData(); + + if ($cdiField = Mage::getStoreConfig('payment/ebanx_settings/cdi_field')) { + if ($cdiField === 'taxvat') { + return $this->order->getCustomerTaxvat(); + } + + if ($customer[$cdiField]) { + return $customer[$cdiField]; + } + } + + return $customer['ebanx-document'][$methodCode]; + } + + public function getPeruvianDocumentNumber($methodCode) + { + $customer = $this->getCustomerData(); + + if ($dniField = Mage::getStoreConfig('payment/ebanx_settings/dni_field_pe')) { + if ($dniField === 'taxvat') { + return $this->order->getCustomerTaxvat(); + } + + if ($customer[$dniField]) { + return $customer[$dniField]; + } + } + + return $customer['ebanx-document'][$methodCode]; + } + + public function getPersonType($document) + { + $document = str_replace(array('.', '-', '/'), '', $document); + + if ($this->getCustomerData()['country_id'] !== 'BR' || strlen($document) < 14) { + return Person::TYPE_PERSONAL; + } + + return Person::TYPE_BUSINESS; + } + + public function errorLog($data) + { + $this->log($data, 'ebanx_error'); + } + + public function log($data, $filename = 'ebanx', $extension = '.log') + { + $isLogEnabled = Mage::getStoreConfig('payment/ebanx_settings/debug_log') === '1'; + + if (!$isLogEnabled) return; + + Mage::log($data, null, $filename . $extension, true); + } + + /** + * Splits address in street name, house number and addition + * + * @param string $address Address to be split + * @return array + */ + public function split_street($address) + { + $result = preg_match('/^([^,\-\/\#0-9]*)\s*[,\-\/\#]?\s*([0-9]+)\s*[,\-\/]?\s*([^,\-\/]*)(\s*[,\-\/]?\s*)([^,\-\/]*)$/', $address, $matches); + if ($result === false) { + throw new \RuntimeException(sprintf('Problems trying to parse address: \'%s\'', $address)); + } + if ($result === 0) { + return array( + 'streetName' => $address, + 'houseNumber' => 'S/N', + 'additionToAddress' => '' + ); + } + $street_name = $matches[1]; + $house_number = $matches[2]; + $addition_to_address = $matches[3] . $matches[4] . $matches[5]; + if (empty($street_name)) { + $street_name = $matches[3]; + $addition_to_address = $matches[5]; + } + return array( + 'streetName' => $street_name, + 'houseNumber' => $house_number ?: 'S/N', + 'additionToAddress' => $addition_to_address + ); + } + + public function getVoucherUrlByHash($hash, $format = 'basic') + { + $res = $this->getPaymentByHash($hash); + + if ($res['status'] !== 'SUCCESS') { + return; + } + + $payment = $res['payment']; + + switch ($payment['payment_type_code']) { + case 'boleto': + $url = $payment['boleto_url']; + break; + case 'pagoefectivo': + $url = $payment['cip_url']; + break; + case 'oxxo': + $url = $payment['oxxo_url']; + break; + case 'baloto': + $url = $payment['baloto_url']; + break; + case 'spei': + $url = $payment['spei_url']; + break; + case 'rapipago': + $url = $payment['voucher_url']; + break; + case 'pagofacil': + $url = $payment['voucher_url']; + break; + case 'cupon': + $url = $payment['voucher_url']; + break; + default: + $url = ''; + } + + if ('mobile' == $format) { + return "{$url}&target_device=mobile"; + } + + return "{$url}&format={$format}"; + } + + public function getPaymentByHash($hash) + { + $ebanx = Mage::getSingleton('ebanx/api')->ebanx(); + + return $ebanx->paymentInfo()->findByHash($hash); + } + + public function getLocalAmountWithTax($currency, $value) + { + $ebanx = Mage::getSingleton('ebanx/api')->ebanx(); + + return $ebanx->exchange()->siteToLocalWithTax($currency, $value); + } + + public function getLocalAmountWithoutTax($currency, $value) + { + $ebanx = Mage::getSingleton('ebanx/api')->ebanx(); + + return $ebanx->exchange()->siteToLocal($currency, $value); + } + + public function hasToShowInlineIcon() + { + return Mage::getStoreConfig('payment/ebanx_settings/payment_methods_visualization'); + } + + public function isEbanxMethod($code) { + $ebanxMethods = array( + 'ebanx_cc_br', + 'ebanx_boleto', + 'ebanx_tef', + 'ebanx_wallet', + 'ebanx_sencillito', + 'ebanx_servipag', + 'ebanx_webpay', + 'ebanx_multicaja', + 'ebanx_pse', + 'ebanx_baloto', + 'ebanx_cc_co', + 'ebanx_cc_mx', + 'ebanx_dc_mx', + 'ebanx_oxxo', + 'ebanx_spei', + 'ebanx_safetypay', + 'ebanx_pagoefectivo', + 'ebanx_cc_ar', + 'ebanx_rapipago', + 'ebanx_pagofacil', + 'ebanx_otroscupones', + 'ebanx_safetypay_ec', + ); + return in_array($code, $ebanxMethods); + } + + /** + * @param $methodCode + * @return mixed + */ + public function getDocumentFieldsRequiredForMethod($methodCode) + { + $methodsToFields = array( + // Brazil + 'ebanx_boleto' => array('cpf_field', 'cnpj_field'), + 'ebanx_tef' => array('cpf_field', 'cnpj_field'), + 'ebanx_wallet' => array('cpf_field', 'cnpj_field'), + 'ebanx_cc_br' => array('cpf_field', 'cnpj_field'), + // Chile + 'ebanx_sencillito' => array(), + 'ebanx_servipag' => array(), + 'ebanx_webpay' => array('rut_field'), + 'ebanx_multicaja' => array(), + // Colombia + 'ebanx_baloto' => array(), + 'ebanx_pse' => array(), + 'ebanx_cc_co' => array('dni_field'), + // Mexico + 'ebanx_oxxo' => array(), + 'ebanx_spei' => array(), + 'ebanx_cc_mx' => array(), + 'ebanx_dc_mx' => array(), + // Peru + 'ebanx_pagoefectivo' => array('dni_field_pe'), + 'ebanx_safetypay' => array('dni_field_pe'), + // Argentina + 'ebanx_cc_ar' => array('cdi_field'), + 'ebanx_rapipago' => array('cdi_field'), + 'ebanx_pagofacil' => array('cdi_field'), + 'ebanx_otroscupones' => array('cdi_field'), + // Ecuador + 'ebanx_safetypay_ec' => array(), + ); + + return $methodsToFields[$methodCode]; + } + + public function getSandboxWarningText() + { + $countryCode = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress()->getCountry(); + $country = Mage::helper('ebanx')->transformCountryCodeToName($countryCode); + + if($country === Country::BRAZIL){ + return array( + 'alert' => 'Ainda estamos testando esse tipo de pagamento. Por isso, a sua compra não será cobrada nem enviada.', + 'tag' => 'EM TESTE', + ); + } + + return array( + 'alert' => 'Todavia estamos probando este método de pago. Por eso su compra no sera cobrada ni enviada.', + 'tag' => 'EN PRUEBA', + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Helper/Error.php b/app/code/community/Ebanx/Gateway/Helper/Error.php new file mode 100644 index 00000000..d27031fa --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Helper/Error.php @@ -0,0 +1,181 @@ +errors = $this->setupErrors(); + } + + public function setupErrors() + { + return array( + 'pt-br' => array( + 'GENERAL' => 'Não foi possível concluir a compra. Por favor, tente novamente ou entre em contato com o site.', + 'BP-DR-13' => 'Ei, você esqueceu de preencher o seu nome.', + 'BP-DR-14' => 'Epa, o seu nome está com mais caracteres do que o limite permitido. Procure abreviá-lo ou então coloque apenas um sobrenome.', + 'BP-DR-15' => 'Espera aí! Faltou preencher o e-mail.', + 'BP-DR-17' => 'Desculpa, mas o e-mail enviado é inválido.', + 'BP-DR-19' => 'Ei, precisamos da sua data de nascimento.', + 'BP-DR-20' => 'A sua data de nascimento deve estar formatada em dd/mm/aaaa.', + 'BP-DR-21' => 'Ei, você precisa ter mais de 16 anos para realizar essa transação.', + 'BP-DR-22' => 'Desculpa, mas o documento enviado é inválido.', + 'BP-DR-23' => 'Preencha com um documento válido:', + 'BP-DR-24' => 'Falta pouco! Preencha o seu CEP.', + 'BP-DR-25' => 'Ei, você esqueceu de informar o seu endereço.', + 'BP-DR-26' => 'Não se esqueça de preencher o número do seu endereço.', + 'BP-DR-27' => 'Ei, você esqueceu de preencher a sua cidade.', + 'BP-DR-28' => 'Ops, faltou preencher o Estado.', + 'BP-DR-29' => 'Confira uma lista com os códigos dos estados brasileiros: https://goo.gl/qCk2V.', + 'BP-DR-31' => 'Preencher o telefone é obrigatório, beleza?', + 'BP-DR-32' => 'O número de telefone informado é inválido.', + 'BP-DR-34' => 'Infelizmente o número de parcelas selecionado não está disponível. Tente de novo com outro número.', + 'BP-DR-39' => 'O documento precisa estar ativo para completar essa transação. Entre em contato com o suporte do EBANX (suporte@ebanx.com) para mais informações.', + 'BP-DR-40' => 'Ops, você quer fazer uma transação que ultrapassa o limite permitido pelo EBANX. Tente de novo com um valor menor.', + 'BP-DR-42' => 'Como a sua transação é com CNPJ, é preciso preencher o responsável.', + 'BP-DR-43' => 'Ei, você esqueceu de preencher o seu nome.', + 'BP-DR-44' => 'Ops, faltou informar o número do seu documento.', + 'BP-DR-45' => 'Ei, você esqueceu de preencher a sua data de nascimento.', + 'BP-DR-46' => 'O documento precisa estar ativo para completar essa transação. Entre em contato com o suporte do EBANX (suporte@ebanx.com) para mais informações.', + 'BP-DR-49' => 'Ei, você esqueceu de preencher o número do seu cartão.', + 'BP-DR-51' => 'Ei, faltou uma informação aqui! Preencha com o nome que aparece em seu cartão.', + 'BP-DR-54' => 'Ops, faltou o CVV do cartão.', + 'BP-DR-56' => 'Ei, faltou preencher a data de vencimento do cartão.', + 'BP-DR-68' => 'Ei, você esqueceu de preencher o número da sua conta.', + 'BP-DR-69' => 'Ops! Você excedeu o número de caracteres permitidos, preencha novamente.', + 'BP-DR-70' => 'Ei, você esqueceu de preencher o número da sua agência.', + 'BP-DR-71' => 'Você excedeu o número de caracteres permitidos para o código do banco, preencha novamente.', + 'BP-DR-75' => 'O número do cartão informado é inválido. Confira se não houve um erro de digitação e tente de novo.', + 'BP-DR-83' => 'Apenas cartões do seu país de origem são permitidos para essa transação.', + 'BP-DR-84' => 'Seu pagamento já foi registrado, não precisa tentar de novo.', + 'BP-DR-90' => 'Ops, aconteceu algum problema. Entre em contato com o EBANX pelo e-mail suporte@ebanx.com para mais informações.', + 'BP-DR-93' => 'Ops, ocorreu um erro inesperado, tente novamente em alguns minutos.', + 'BP-DR-95' => 'Ops, confira se você preencheu mesmo com o seu nome e não com o número do cartão.', + 'BP-DR-97' => 'Você não pode parcelar suas compras com cartões pré-pagos. O que acha de tentar com um cartão de crédito?', + 'BP-DR-98' => 'O país do pagamento precisa ser igual ao seu país de residência, beleza?', + 'BP-DR-100' => 'Cartões de débito não realizam pagamentos parcelados, mas você pode tentar de novo com um cartão de crédito. :)', + 'BP-DR-101' => 'Ops! Esse cartão não está liberado para fazer compras na internet. Entre em contato com o seu banco para mais informações.', + 'BP-DR-102' => 'Ops, algo deu errado. Tente novamente com outro cartão.', + 'BP-R-12' => 'Desculpa, mas o número de parcelas selecionado não está disponível. :/', + 'BP-R-13' => 'O valor de cada parcela deve ser maior ou igual a R$X. Tente de novo com outro número de parcelas. :)', + 'BP-R-28' => 'Ops! O número de parcelas que você escolheu não está disponível para esse valor. Tente de novo com outra opção de parcela.', + 'BP-R-29' => 'O número de parcelas selecionado não está disponível. Tente de novo com outra opção de parcela.', + 'BP-R-30' => 'O número de parcelas que você selecionou não está disponível para essa compra. Tente de novo com outro número de parcela.', + 'BP-R-4' => 'Ops, faltou preencher o seu nome.', + 'BP-R-5' => 'Ei, faltou informar o seu e-mail.', + 'BP-ZIP-1' => 'Ei, você esqueceu de nos informar o seu CEP.', + 'BP-ZIP-2' => 'O CEP enviado não é válido. Por favor, tente de novo.', + 'BP-ZIP-3' => 'O CEP enviado é inválido ou inexistente. Por favor, tente de novo.', + 'MISSING-DEVICE-FINGERPRINT' => 'Algo aconteceu e não conseguimos concluir a sua compra. Por favor tente novamente.', + 'MISSING-CVV' => 'Por favor digite o CVV do seu cartão de crédito.', + 'MISSING-INSTALMENTS' => 'Por favor escolha em quantas parcelas você quer pagar.', + 'MISSING-BANK-NAME' => 'Escolha um banco que deseja efetuar a sua compra.', + 'INVALID-SAFETYPAY-TYPE' => 'Escolha uma opção para o método de pagamento SafetyPay.', + 'INVALID-FIELDS' => 'Alguns campos não foram preenchidos corretamente. Por favor, verifique e tente novamente.', + 'INVALID-BILLING-COUNTRY' => 'Por favor, escolha um país.', + 'INVALID-ADDRESS' => 'Insira o seu endereço completo com o número da casa, apartamento ou estabelecimento.', + 'SANDBOX-INVALID-CC-NUMBER' => 'Detectamos que você está em modo Sandbox e por isso só permitimos apenas alguns números de cartões. Você pode utilizar um dos nossos cartões de teste acessando a EBANX Developer\'s Academy.', + 'CC-NOK' => 'Houve um problema com seu cartão de crédito, entre em contato com o emissor do cartão.', + 'CC-RETRY' => 'Ocorreu um problema com seu cartão, tente novamente em alguns momentos.', + ), + 'es' => array( + 'GENERAL' => 'No pudimos concluir tu compra. Por favor intenta nuevamente o entra en contacto con el sitio web.', + 'BP-DR-13' => '¡Ey!, olvidaste ingresar tu nombre.', + 'BP-DR-14' => '¡Ey!, su nombre tiene más caracteres que el límite permitido. Busque abreviado o coloque sólo un apellido.', + 'BP-DR-15' => '¡Espera! Falto ingresar tu correo electrónico.', + 'BP-DR-17' => 'Disculpa, pero ese correo electrónico no es válido.', + 'BP-DR-19' => '¡Ey!, no dejes de informar tu fecha de nacimiento.', + 'BP-DR-20' => 'La fecha de nacimiento debe seguir este formato: dd/mm/aaaa.', + 'BP-DR-21' => '¡Ey!, debes ser mayor de 16 años para realizar esa transacción.', + 'BP-DR-22' => '¡Lo sentimos!, pero el documento enviado no es válido.', + 'BP-DR-23' => 'Ingresa un documento con validez.', + 'BP-DR-24' => '¡Falta poco!, informa tu Código Postal.', + 'BP-DR-25' => '¡Ey!, has olvidado informar tu domicilio.', + 'BP-DR-26' => 'No dejes de informar el número de domicilio.', + 'BP-DR-27' => '¡Ups!, falto informar la Ciudad.', + 'BP-DR-28' => '¡Ups!, falto informar el Estado.', + 'BP-DR-29' => '-', + 'BP-DR-31' => '¡Ojo! Es obligatorio informar un número de teléfono.', + 'BP-DR-32' => 'El número de teléfono que informaste no es válido.', + 'BP-DR-34' => 'México: El número de mensualidades seleccionado no está disponible. Inténtelo de nuevo con otro número de mensualides.', + 'BP-DR-39' => '-', + 'BP-DR-40' => '¡Ups!, la transacción que deseas realizar ultrapasa el valor límite permitido por EBANX. Intenta realizar una transacción de menor valor.', + 'BP-DR-42' => 'Como tu transacción es con (Documento de cada país), es necesario señalas al responsable.', + 'BP-DR-43' => '¡Ey!, olvidaste ingresar tu nombre.', + 'BP-DR-44' => '¡Ups!, olvidaste informar el número de tu documento.', + 'BP-DR-45' => '¡Ey!, olvidaste informar la fecha de tu nacimiento.', + 'BP-DR-46' => '-', + 'BP-DR-49' => '¡Ey!, olvidaste introducir el número de tu tarjeta.', + 'BP-DR-51' => '¡Ups!, faltó llenar este campo. Llénalo con el nombre que aparece en tu tarjeta.', + 'BP-DR-54' => '¡Ups!, falto el CVV de la tarjeta.', + 'BP-DR-56' => '¡Ey!, has omitido la fecha de vencimiento de la tarjeta.', + 'BP-DR-68' => '¡Ey!, olvidaste ingresar el número de tu cuenta.', + 'BP-DR-69' => '¡Ups!, excediste el número de caracteres permitidos, inténtalo nuevamente.', + 'BP-DR-70' => '¡Ey!, olvidaste llenar el campo "agencia".', + 'BP-DR-71' => 'Excediste el número de caracteres permitidos para el código de el banco, inténtalo nuevamente.', + 'BP-DR-75' => 'El número de la tarjeta es invalido. Confirma si no hay un error de escritura y vuelva a intentarlo.', + 'BP-DR-83' => 'Esa tarjeta no es permitida para completar la transacción.', + 'BP-DR-84' => 'La transacción ha sido registrada, no es necesario volver a intentar.', + 'BP-DR-90' => '¡Ups!, ocurrió algún problema. Entra en contacto con EBANX, envía un correo a soporte@ebanx.com para más información.', + 'BP-DR-93' => 'Ocurrió un error inesperado, intentalo de nuevo en unos minutos más.', + 'BP-DR-95' => '¡Ups! Confirma que hallas llenado este campo con tu nombre y no con otros datos de la tarjeta.', + 'BP-DR-97' => 'Pagos en mensualidades no son permitidos para tarjetas de prepago.', + 'BP-DR-98' => '¡Ojo! El país desde el cual realizas tu pago debe corresponder al país donde resides.', + 'BP-DR-100' => 'Pagos en mensualidades no son permitidos para tarjetas de débito.', + 'BP-DR-101' => '¡Lo sentimos!, vuelva a intentarlo con otra tarjeta.', + 'BP-DR-102' => '¡Ups!, algo salió mal, vuelve a intentarlo con otra tarjeta.', + 'BP-R-12' => '¡Lo sentimos!, el número de mensualidades seleccionado no está disponible.', + 'BP-R-13' => 'El valor de cada mensualidad debe ser igual o mayor a $X.', + 'BP-R-28' => '¡Ups! El número de mensualidades seleccionado es mayor al permitido.', + 'BP-R-29' => '¡Lo sentimos!, el número de mensualidades seleccionado no está disponible.', + 'BP-R-30' => '¡Lo sentimos!, el número de mensualidades seleccionado no está disponible.', + 'BP-R-4' => '¡Ups!, falto informar tu nombre.', + 'BP-R-5' => '¡Ey!, falto informar tu correo electrónico.', + 'BP-ZIP-1' => '¡Ey!, olvidaste informar el Código Postal.', + 'BP-ZIP-2' => 'El Código Postal informado, no es válido. Por favor, ingresalo nuevamente.', + 'BP-ZIP-3' => 'El Código Postal es invalido o no existe. Por favor, prueba con otro.', + 'MISSING-CARD-PARAMS' => 'Por favor, verifica que la información de la tarjeta esté correcta.', + 'MISSING-DEVICE-FINGERPRINT' => 'Hemos encontrado un error y no fue posible concluir la compra. Por favor intenta de nuevo.', + 'MISSING-CVV' => 'Por favor, introduce el CVV de tu tarjeta de crédito.', + 'MISSING-INSTALMENTS' => 'Por favor, escoge en cuántos meses sin intereses deseas pagar.', + 'MISSING-BANK-NAME' => 'Por favor, escoge el banco para finalizar la compra.', + 'INVALID-SAFETYPAY-TYPE' => 'Por favor, escoge una opción para el método de pago SafetyPay.', + 'INVALID-FIELDS' => 'Algunos campos no fueron llenados correctamente. Por favor verifica e inténtalo de nuevo.', + 'INVALID-BILLING-COUNTRY' => 'Por favor, escoge un país.', + 'INVALID-ADDRESS' => 'Por favor, introduce tu dirección completa. Número de residencia o apartamento.', + 'SANDBOX-INVALID-CC-NUMBER' => 'Detectamos que estás en modo Sandbox y por eso restringimos algunos números de tarjetas. Puedes utilizar una de nuestras tarjetas de prueba accediendo a EBANX Developer\'s Academy.', + 'CC-NOK' => 'Ha habido un problema con su tarjeta de crédito, póngase en contacto con el emisor de la tarjeta.', + 'CC-RETRY' => 'Se ha producido un problema con su tarjeta, vuelva a intentarlo en algunos momentos.', + ), + ); + } + + public function getError($code = 'GENERAL', $country = null) + { + $language = $this->getLanguage($country); + + $isErrorExist = array_key_exists($code, $this->errors[$language]); + + return $isErrorExist ? $this->errors[$language][strtoupper($code)] : $this->errors[$language][$this->generalCode]; + } + + public function getLanguage($country) + { + switch (strtolower($country)) { + case 'br': + return 'pt-br'; + case 'mx': + case 'cl': + case 'co': + case 'pe': + case 'ar': + return 'es'; + break; + default: + return 'pt-br'; + }; + } +} diff --git a/app/code/community/Ebanx/Gateway/Helper/Order.php b/app/code/community/Ebanx/Gateway/Helper/Order.php new file mode 100644 index 00000000..35144fe3 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Helper/Order.php @@ -0,0 +1,56 @@ +getOrderBy('ebanx_payment_hash', $hash); + } + + public function getLegacyOrderByHash($hash) + { + return $this->getOrderBy('ebanx_hash', $hash); + } + + public function getEbanxMagentoOrder($ebanxStatus) + { + $status = array( + 'CO' => Mage::getStoreConfig('payment/ebanx_settings/payment_co_status'), + 'PE' => Mage::getStoreConfig('payment/ebanx_settings/payment_pe_status'), + 'OP' => Mage::getStoreConfig('payment/ebanx_settings/payment_op_status'), + 'CA' => Mage::getStoreConfig('payment/ebanx_settings/payment_ca_status') + ); + + return $status[strtoupper($ebanxStatus)]; + } + + public function getTranslatedOrderStatus($ebanxStatus) + { + $status = array( + 'CO' => 'Confirmed', + 'PE' => 'Pending', + 'OP' => 'Open', + 'CA' => 'Canceled' + ); + + return $status[strtoupper($ebanxStatus)]; + } + + private function getOrderBy($field, $value) + { + $model = Mage::getModel('sales/order_payment') + ->getCollection() + ->setPageSize(1) + ->setCurPage(1) + ->addFieldToFilter($field, $value) + ->load(); + + if ($model->count() !== 1) { + Mage::throwException($this->__('EBANX: Invalid payment hash. We couldn\'t find the order.')); + }; + + $payment = $model->getFirstItem(); + + return Mage::getModel('sales/order')->load($payment->getParentId()); + } +} diff --git a/app/code/community/Ebanx/Gateway/Log/Environment.php b/app/code/community/Ebanx/Gateway/Log/Environment.php new file mode 100644 index 00000000..74c4694b --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Log/Environment.php @@ -0,0 +1,87 @@ + array( + 'name' => 'Magento', + 'version' => $environment->platform->version, + 'theme' => self::get_theme_data(), + 'plugins' => self::get_plugins_data(), + 'store_id' => Mage::app()->getStore()->getWebsiteId(), + ), + 'server' => array( + 'language' => $environment->interpreter, + 'web_server' => $environment->web_server, + 'database_server' => $environment->database_server, + 'os' => $environment->operating_system, + ), + ); + } + + private static function get_environment() { + $environment = new stdClass(); + $environment->platform = new stdClass(); + $environment->platform->name = 'Magento'; + $environment->platform->version = Mage::getVersion(); + + $environment->interpreter = new stdClass(); + $environment->interpreter->name = 'PHP'; + $environment->interpreter->version = PHP_VERSION; + + $environment->web_server = new stdClass(); + $environment->web_server->signature = $_SERVER['SERVER_SIGNATURE']; + + $resource = Mage::getSingleton('core/resource'); + $conn = $resource->getConnection('externaldb_read'); + + $environment->database_server = new stdClass(); + $environment->database_server->name = 'MySQL'; + $environment->database_server->version = $conn->fetchCol('SELECT version() AS version')[0]; + + $environment->operating_system = new stdClass(); + $environment->operating_system->name = PHP_OS; + $environment->operating_system->version = self::extract_version_number_from(php_uname('v')); + + return $environment; + } + + private static function get_plugins_data() { + return (array) array_map(function ($plugin) { + $plugin->status = Mage::helper('core')->isModuleOutputEnabled($plugin->getName()) ? 'enabled' : 'disabled'; + + return $plugin; + }, (array) Mage::getConfig()->getNode('modules')->children()); + } + + private static function get_theme_data() { + return array_map(function ($v) { + return (object) array($v => Mage::getSingleton('core/design_package')->getTheme($v)); + }, array( + 'locale', + 'layout', + 'template', + 'default', + 'frontend', + 'skin' + )); + } + + private static function extract_version_number_from($haystack) { + preg_match( '/((\d)+(\.|\D))+/', $haystack, $version_candidates_array ); + if ( count( $version_candidates_array ) > 0 && strlen( $version_candidates_array[0] ) > 0 ) { + $version_candidates_array[0] = str_replace( '.', '_', $version_candidates_array[0] ); + $version_candidates_array[0] = preg_replace( '/[\W]/', '', $version_candidates_array[0] ); + $version_candidates_array[0] = str_replace( '_', '.', $version_candidates_array[0] ); + $version = $version_candidates_array[0]; + } else { + $version = 'Unknown'; + } + return $version; + } +} diff --git a/app/code/community/Ebanx/Gateway/Log/Logger.php b/app/code/community/Ebanx/Gateway/Log/Logger.php new file mode 100644 index 00000000..6e366ac8 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Log/Logger.php @@ -0,0 +1,69 @@ +getIntegrationKey(); + + $logModel->setEvent($event); + $logModel->setLog(json_encode($log_data)); + $logModel->setIntegrationKey($integrationKey); + + $logModel->save(); + } + + final public static function lastByEvent($event = 'plugin_status_change') { + $logModel = new Ebanx_Gateway_Model_Log(); + + $col = $logModel->getCollection() + ->addFieldToSelect(array('log')) + ->addFieldToFilter('event', $event); + + $col->getSelect() + ->order('id DESC') + ->limit(1); + + return $col; + } + + final public static function delete($col) { + foreach($col as $log) + $log->delete(); + } + + final public static function fetch($integrationKey) { + $logModel = new Ebanx_Gateway_Model_Log(); + + $col = $logModel->getCollection(); + + $col->addFieldToFilter('integration_key', $integrationKey) + ->getSelect() + ->order('id DESC'); + + $res = array(); + + foreach ($col as $log) { + $res[] = $log->getData(); + } + + return array($col ,$res); + } + + /** + * Abstract method that must be overrated by child classes + * + * This method is responsible for receive log data, manage them and send them to method save + * + * @param array $log_data data to be logged. + */ + abstract public static function persist(array $log_data = array()); +} diff --git a/app/code/community/Ebanx/Gateway/Log/Logger/Checkout.php b/app/code/community/Ebanx/Gateway/Log/Logger/Checkout.php new file mode 100644 index 00000000..ccb53dfa --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Log/Logger/Checkout.php @@ -0,0 +1,16 @@ + 'AL', + 'amapa' => 'AP', + 'amazonas' => 'AM', + 'bahia' => 'BA', + 'ceara' => 'CE', + 'distrito federal' => 'DF', + 'espirito santo' => 'ES', + 'goias' => 'GO', + 'maranhao' => 'MA', + 'mato grosso' => 'MT', + 'mato grosso do sul' => 'MS', + 'minas gerais' => 'MG', + 'para' => 'PA', + 'paraiba' => 'PB', + 'parana' => 'PR', + 'pernambuco' => 'PE', + 'piaui' => 'PI', + 'rio de janeiro' => 'RJ', + 'rio grande do norte' => 'RN', + 'rio grande do sul' => 'RS', + 'rondonia' => 'RO', + 'roraima' => 'RR', + 'santa catarina' => 'SC', + 'sao paulo' => 'SP', + 'sergipe' => 'SE', + 'tocantins' => 'TO', + ); + + public function __construct() + { + $this->helper = Mage::helper('ebanx'); + } + + public function transformCard(Varien_Object $data) + { + $gatewayFields = $data->getGatewayFields(); + $instalmentTerms = $data->getInstalmentTerms(); + + $payment = $this->transform($data); + + $selectedCard = $gatewayFields['selected_card']; + $payment->deviceId = $gatewayFields['ebanx_device_fingerprint'][$selectedCard]; + + if (isset($gatewayFields['instalments'])) { + $payment->instalments = $gatewayFields['instalments']; + $term = $instalmentTerms[$gatewayFields['instalments'] - 1]; + $payment->amountTotal = $term->baseAmount * $term->instalmentNumber; + } + + $code = $data->getPaymentType(); + + $payment->card = new Card(array( + 'autoCapture' => $this->shouldAutoCapture(), + 'cvv' => $gatewayFields[$code . '_cid'][$selectedCard], + 'dueDate' => $this->transformDueDate($gatewayFields, $code), + 'name' => $gatewayFields[$code . '_name'][$selectedCard], + 'token' => $gatewayFields['ebanx_token'][$selectedCard], + 'type' => $gatewayFields['ebanx_brand'][$selectedCard], + )); + + if ($data->getBillingAddress()->getCountry() === 'AR') { + $payment->card->type = 'mastercard'; + } + + return $payment; + } + + /** + * @return boolean + */ + private function shouldAutoCapture() { + return Mage::getStoreConfig('payment/ebanx_settings/auto_capture') === '1'; + } + + /** + * @param Varien_Object $data + * @return Payment + */ + public function transform(Varien_Object $data) + { + return new Payment(array( + 'type' => $data->getEbanxMethod(), + 'amountTotal' => $data->getAmountTotal(), + 'merchantPaymentCode' => $data->getMerchantPaymentCode(), + 'orderNumber' => $data->getOrderId(), + 'dueDate' => new \DateTime($data->getDueDate()), + 'address' => $this->transformAddress($data->getBillingAddress(), $data), + 'person' => $this->transformPerson($data->getPerson(), $data), + 'responsible' => $this->transformPerson($data->getPerson(), $data), + 'items' => $this->transformItems($data->getItems(), $data), + 'riskProfileId' => $this->transformRiskProfileId(), + )); + } + + /** + * @param Varien_Object $address + * @param Varien_Object $data + * @return Address + */ + public function transformAddress($address, $data) + { + $street = $this->helper->split_street($address->getStreet1()); + $state = $address->getRegion(); + + $streetNumberField = Mage::getStoreConfig('payment/ebanx_settings/street_number_field'); + if ($streetNumberField && isset($address->getData()[$streetNumberField])) { + $street['houseNumber'] = $address->getData()[$streetNumberField]; + } + + if ($address->getCountry() === 'BR') { + $state = preg_replace('~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml|caron);~i', '$1', htmlentities($state, ENT_QUOTES, 'UTF-8')); + if (array_key_exists(strtolower($state), $this->states)){ + $state = $this->states[strtolower($state)]; + } + } + + return new Address(array( + 'address' => $street['streetName'], + 'streetNumber' => $street['houseNumber'], + 'city' => $address->getCity(), + 'country' => $this->helper->transformCountryCodeToName($address->getCountry()), + 'state' => $state, + 'streetComplement' => $address->getStreet2(), + 'zipcode' => $address->getPostcode() + )); + } + + public function transformPerson($person, $data) + { + $document = $this->helper->getDocumentNumber($data->getOrder(), $data); + + $email = $person->getCustomerEmail() ?: $data->getBillingAddress()->getEmail(); + + $name = $person->getCustomerFirstname() || $person->getCustomerLastname() + ? $person->getCustomerFirstname() . ' ' . $person->getCustomerLastname() + : $data->getBillingAddress()->getName(); + + return new Person(array( + 'type' => $this->helper->getPersonType($document), + 'document' => $document, + 'documentType' => $this->helper->getDocumentType($data->getBillingAddress()->getCountry()), + 'email' => $email, + 'ip' => $data->getRemoteIp(), + 'name' => $name, + 'phoneNumber' => $data->getBillingAddress()->getTelephone() + )); + } + + public function transformItems($items, $data) + { + $itemsData = array(); + + foreach ($items as $item) { + $product = $item->getProduct(); + + $itemsData[] = new Item(array( + 'sku' => $item->getSku(), + 'name' => $item->getName(), + 'unitPrice' => $product->getPrice(), + 'quantity' => $item->getQtyToInvoice() + )); + } + + return $itemsData; + } + + /** + * @param array $gatewayFields + * @param string $code + * + * @return bool|DateTime + */ + private function transformDueDate($gatewayFields, $code) { + $selectedCard = $gatewayFields['selected_card']; + $month = 1; + if (array_key_exists($code . '_exp_month', $gatewayFields) && is_array($gatewayFields[$code . '_exp_month']) && array_key_exists($selectedCard, $gatewayFields[$code . '_exp_month'])) { + $month = $gatewayFields[$code . '_exp_month'][$selectedCard] ?: 1; + } + + $year = 2120; + if (array_key_exists($code . '_exp_year', $gatewayFields) && is_array($gatewayFields[$code . '_exp_year']) && array_key_exists($selectedCard, $gatewayFields[$code . '_exp_year'])) { + $year = $gatewayFields[$code . '_exp_year'][$selectedCard] ?: 2120; + } + + return DateTime::createFromFormat( 'n-Y', $month . '-' . $year ); + } + + private function transformRiskProfileId() { + $version = 'Mx' . Mage::getConfig()->getNode('modules/Ebanx_Gateway/version'); + + return preg_replace('/\./', 'x', $version); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Api.php b/app/code/community/Ebanx/Gateway/Model/Api.php new file mode 100644 index 00000000..afd04866 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Api.php @@ -0,0 +1,74 @@ +ebanx = EBANX($this->getConfig(), $this->getCreditCardConfig()); + } + + public function getConfig() + { + return new Config(array( + 'taxesOnMerchant' => Mage::getStoreConfig('payment/ebanx_settings/iof_local_amount') === '0', + 'integrationKey' => Mage::helper('ebanx')->getLiveIntegrationKey(), + 'sandboxIntegrationKey' => Mage::helper('ebanx')->getSandboxIntegrationKey(), + 'isSandbox' => Mage::helper('ebanx')->isSandboxMode(), + 'baseCurrency' => Mage::app()->getStore()->getCurrentCurrencyCode(), + 'notificationUrl' => Mage::getUrl('ebanx/payment/notify/'), + 'redirectUrl' => Mage::getUrl('checkout/onepage/success'), + 'userValues' => array( + 1 => 'from_magento', + 3 => (string)Mage::getConfig()->getNode('modules/Ebanx_Gateway/version'), + ), + )); + } + + /** + * @return CreditCardConfig + */ + private function getCreditCardConfig() + { + $creditCardConfig = new CreditCardConfig(array( + 'maxInstalments' => Mage::helper('ebanx')->getMaxInstalments(), + 'minInstalmentAmount' => Mage::helper('ebanx')->getMinInstalmentValue(), + )); + + $interestRate = unserialize(Mage::helper('ebanx')->getInterestRate()); + usort($interestRate, function ($value, $previous) { + if ($value['instalments'] === $previous['instalments']) { + return 0; + } + + return ($value['instalments'] < $previous['instalments']) ? -1 : 1; + }); + + + for ($i = 1; $i <= Mage::helper('ebanx')->getMaxInstalments(); $i++) { + foreach ($interestRate as $interestConfig) { + if ($i <= $interestConfig['instalments']) { + $creditCardConfig->addInterest($i, $interestConfig['interest']); + break; + } + } + } + return $creditCardConfig; + } + + public function ebanx() + { + return $this->ebanx; + } + + public function ebanxCreditCard() + { + return $this->ebanx; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Argentina/Creditcard.php b/app/code/community/Ebanx/Gateway/Model/Argentina/Creditcard.php new file mode 100644 index 00000000..66b530b2 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Argentina/Creditcard.php @@ -0,0 +1,23 @@ +getCode(), explode(',', $this->configs['payment_methods_argentina'])); + } + + /** + * @return string + */ + protected function getCountry() + { + return Country::ARGENTINA; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Argentina/Otroscupones.php b/app/code/community/Ebanx/Gateway/Model/Argentina/Otroscupones.php new file mode 100644 index 00000000..37eb2e37 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Argentina/Otroscupones.php @@ -0,0 +1,20 @@ +gateway = $this->ebanx->otrosCupones(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_argentina'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Argentina/Pagofacil.php b/app/code/community/Ebanx/Gateway/Model/Argentina/Pagofacil.php new file mode 100644 index 00000000..0a6e51b9 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Argentina/Pagofacil.php @@ -0,0 +1,20 @@ +gateway = $this->ebanx->pagofacil(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_argentina'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Argentina/Rapipago.php b/app/code/community/Ebanx/Gateway/Model/Argentina/Rapipago.php new file mode 100644 index 00000000..9a003624 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Argentina/Rapipago.php @@ -0,0 +1,20 @@ +gateway = $this->ebanx->rapipago(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_argentina'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Brazil/Boleto.php b/app/code/community/Ebanx/Gateway/Model/Brazil/Boleto.php new file mode 100644 index 00000000..d254f7bc --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Brazil/Boleto.php @@ -0,0 +1,27 @@ +gateway = $this->ebanx->boleto(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_brazil'])); + } + + public function persistPayment() + { + parent::persistPayment(); + $this->payment->setEbanxBarCode($this->result['payment']['boleto_barcode']); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Brazil/Creditcard.php b/app/code/community/Ebanx/Gateway/Model/Brazil/Creditcard.php new file mode 100644 index 00000000..d15ee268 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Brazil/Creditcard.php @@ -0,0 +1,23 @@ +getCode(), explode(',', $this->configs['payment_methods_brazil'])); + } + + /** + * @return string + */ + protected function getCountry() + { + return Country::BRAZIL; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Brazil/Tef.php b/app/code/community/Ebanx/Gateway/Model/Brazil/Tef.php new file mode 100644 index 00000000..d2b72123 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Brazil/Tef.php @@ -0,0 +1,31 @@ +gateway = $this->ebanx->tef(); + } + + public function transformPaymentData() + { + parent::transformPaymentData(); + + $bank = Mage::app()->getRequest()->getPost('ebanx_tef'); + $bankCode = Mage::helper('ebanx')->transformTefToBankName($bank); + + $this->paymentData->bankCode = $bankCode; + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_brazil'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Brazil/Wallet.php b/app/code/community/Ebanx/Gateway/Model/Brazil/Wallet.php new file mode 100644 index 00000000..ac372975 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Brazil/Wallet.php @@ -0,0 +1,21 @@ +gateway = $this->ebanx->ebanxAccount(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_brazil'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Capture.php b/app/code/community/Ebanx/Gateway/Model/Capture.php new file mode 100644 index 00000000..0c9b7f1c --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Capture.php @@ -0,0 +1,43 @@ +ebanx = Mage::getSingleton('ebanx/api')->ebanx(); + $this->payment = $observer + ->getEvent() + ->getInvoice() + ->getOrder() + ->getPayment(); + $hash = $this->payment->getEbanxPaymentHash(); + $method_code = $this->payment->getMethodInstance()->getCode(); + + if (!isset($this->payment) + || !isset($hash) + || strpos($method_code, "ebanx_cc") === false + || !$this->isPaymentPending($hash)) { + return; + } + + $this->ebanx->creditCard()->captureByHash($hash); + } + + private function isPaymentPending($hash) + { + $helper = Mage::helper('ebanx/order'); + $isSandbox = $this->payment->getEbanxEnvironment() === 'sandbox'; + $payment = $this->ebanx->paymentInfo()->findByHash($hash, $isSandbox); + + if ($payment['status'] !== 'SUCCESS') { + throw new Ebanx_Gateway_Exception($helper->__('EBANX: Payment doesn\'t exist.')); + } + + return $payment['payment']['status'] === 'PE'; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Chile/Multicaja.php b/app/code/community/Ebanx/Gateway/Model/Chile/Multicaja.php new file mode 100644 index 00000000..83d838ce --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Chile/Multicaja.php @@ -0,0 +1,21 @@ +gateway = $this->ebanx->multicaja(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_chile'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Chile/Sencillito.php b/app/code/community/Ebanx/Gateway/Model/Chile/Sencillito.php new file mode 100644 index 00000000..ef7c6ac0 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Chile/Sencillito.php @@ -0,0 +1,21 @@ +gateway = $this->ebanx->sencillito(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_chile'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Chile/Servipag.php b/app/code/community/Ebanx/Gateway/Model/Chile/Servipag.php new file mode 100644 index 00000000..154b8e3e --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Chile/Servipag.php @@ -0,0 +1,21 @@ +gateway = $this->ebanx->servipag(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_chile'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Chile/Webpay.php b/app/code/community/Ebanx/Gateway/Model/Chile/Webpay.php new file mode 100644 index 00000000..2418801d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Chile/Webpay.php @@ -0,0 +1,21 @@ +gateway = $this->ebanx->webpay(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_chile'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Colombia/Baloto.php b/app/code/community/Ebanx/Gateway/Model/Colombia/Baloto.php new file mode 100644 index 00000000..4fad68e5 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Colombia/Baloto.php @@ -0,0 +1,21 @@ +gateway = $this->ebanx->baloto(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_colombia'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Colombia/Creditcard.php b/app/code/community/Ebanx/Gateway/Model/Colombia/Creditcard.php new file mode 100644 index 00000000..29589480 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Colombia/Creditcard.php @@ -0,0 +1,23 @@ +getCode(), explode(',', $this->configs['payment_methods_colombia'])); + } + + /** + * @return string + */ + protected function getCountry() + { + return Country::COLOMBIA; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Colombia/Pse.php b/app/code/community/Ebanx/Gateway/Model/Colombia/Pse.php new file mode 100644 index 00000000..8e735617 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Colombia/Pse.php @@ -0,0 +1,30 @@ +gateway = $this->ebanx->eft(); + } + + public function transformPaymentData() + { + parent::transformPaymentData(); + + $bankCode = Mage::app()->getRequest()->getPost('ebanx_pse_bank'); + + $this->paymentData->bankCode = $bankCode; + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_colombia'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Ecuador/Safetypay.php b/app/code/community/Ebanx/Gateway/Model/Ecuador/Safetypay.php new file mode 100644 index 00000000..337760a5 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Ecuador/Safetypay.php @@ -0,0 +1,24 @@ +getCode(), explode(',', $this->configs['payment_methods_ecuador'])); + } + + /** + * @return string + */ + protected function getCountry() + { + return Country::ECUADOR; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Import.php b/app/code/community/Ebanx/Gateway/Model/Import.php new file mode 100644 index 00000000..23ca31b3 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Import.php @@ -0,0 +1,25 @@ +ebanx = Mage::getSingleton('ebanx/api')->ebanx(); + $rate = $this->ebanx->exchange()->fetchRate($currencyFrom, $currencyTo); + + return $rate ?: null; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Lead.php b/app/code/community/Ebanx/Gateway/Model/Lead.php new file mode 100644 index 00000000..83ca2b0f --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Lead.php @@ -0,0 +1,9 @@ +_init('ebanx/lead'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Legacy/Express.php b/app/code/community/Ebanx/Gateway/Model/Legacy/Express.php new file mode 100644 index 00000000..22ab5c85 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Legacy/Express.php @@ -0,0 +1,9 @@ +_init('ebanx/log'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Mexico/Creditcard.php b/app/code/community/Ebanx/Gateway/Model/Mexico/Creditcard.php new file mode 100644 index 00000000..88f37591 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Mexico/Creditcard.php @@ -0,0 +1,23 @@ +getCode(), explode(',', $this->configs['payment_methods_mexico'])); + } + + /** + * @return string + */ + protected function getCountry() + { + return Country::MEXICO; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Mexico/Debitcard.php b/app/code/community/Ebanx/Gateway/Model/Mexico/Debitcard.php new file mode 100644 index 00000000..45979d3d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Mexico/Debitcard.php @@ -0,0 +1,33 @@ +gateway = $this->ebanx->debitCard(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_mexico'])); + } + + public function setupData() + { + parent::setupData(); + + $this->data->setGatewayFields(Mage::app()->getRequest()->getPost('payment')); + $this->data->setPaymentType('dc'); + } + + public function transformPaymentData() + { + $this->paymentData = $this->adapter->transformCard($this->data); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Mexico/Oxxo.php b/app/code/community/Ebanx/Gateway/Model/Mexico/Oxxo.php new file mode 100644 index 00000000..95394e00 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Mexico/Oxxo.php @@ -0,0 +1,20 @@ +gateway = $this->ebanx->oxxo(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_mexico'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Mexico/Spei.php b/app/code/community/Ebanx/Gateway/Model/Mexico/Spei.php new file mode 100644 index 00000000..63bbdb68 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Mexico/Spei.php @@ -0,0 +1,20 @@ +gateway = $this->ebanx->spei(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_mexico'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Observer.php b/app/code/community/Ebanx/Gateway/Model/Observer.php new file mode 100644 index 00000000..3a1783d6 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Observer.php @@ -0,0 +1,96 @@ +isModuleOutputEnabled('Ebanx_Gateway'); + $toData = $to ? array('to' => 'enabled') : array('to' => 'disabled'); + $prev_status = null; + + $col = Ebanx_Gateway_Log_Logger::lastByEvent(); + + foreach ($col as $item) { + $itemData = $item->getData(); + if (!empty($itemData)) { + $prev_status = json_decode($itemData['log'])->to; + } + } + + if (is_null($prev_status) || (!$to && $prev_status === 'enabled') || ($to && $prev_status !== 'enabled')) { + Ebanx_Gateway_Log_Logger_PluginStatusChange::persist($toData); + } + } + + public function observeConfigSection($observer) + { + Ebanx_Gateway_Log_Logger_SettingsChange::persist(array( + 'settings' => Mage::getStoreConfig('payment/ebanx_settings') + )); + + $store = Mage::app()->getStore(); + $leadModel = new Ebanx_Gateway_Model_Lead(); + + $lead = $leadModel->load($store->getWebsiteId(), 'id_store')->getData(); + + if (!empty($lead)) { + $helperEbanxData = Mage::helper('ebanx/data'); + $leadData = array( + 'id' => $lead['id_lead'], + 'integration_key' => $helperEbanxData->getIntegrationKey(), + 'site_url' => Mage::getBaseUrl(), + 'type' => 'Magento', + ); + } else { + $user = Mage::getSingleton('admin/session')->getUser(); + $leadData = array( + 'user_email' => $user->getEmail(), + 'user_last_name' => $user->getLastname(), + 'user_first_name' => $user->getFirstname(), + 'site_email' => Mage::getStoreConfig('trans_email/ident_sales/email'), + 'site_url' => Mage::getBaseUrl(), + 'site_name' => $store->getFrontendName(), + 'site_language' => Mage::app()->getLocale()->getLocaleCode(), + 'magento_version' => Mage::getVersion(), + 'type' => 'Magento', + ); + } + + $data = json_encode( + array('lead' => $leadData) + ); + + $this->doCurl($data, $store->getWebsiteId(), (isset($user))); + } + + private function doCurl($data, $storeId, $new = true) + { + $ch = curl_init('https://dashboard.ebanx.com/api/lead'); + + curl_setopt_array($ch, array( + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $data, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTPHEADER => array( + 'Content-Type: application/json', + ), + )); + + $transfer = curl_exec($ch); + $error = curl_error($ch); + + if ($new && empty($error)) { + $leadInfo = json_decode($transfer, true); + + if (isset($leadInfo['id'])) { + $leadModel = new Ebanx_Gateway_Model_Lead(); + $leadModel->setIdLead($leadInfo['id']); + $leadModel->setIdStore($storeId); + + $leadModel->save(); + } + } + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Payment/Creditcard.php b/app/code/community/Ebanx/Gateway/Model/Payment/Creditcard.php new file mode 100644 index 00000000..2b97af4d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Payment/Creditcard.php @@ -0,0 +1,136 @@ +ebanx = Mage::getSingleton('ebanx/api')->ebanxCreditCard(); + $this->gateway = $this->ebanx->creditCard(); + } + + public function getInstalmentTerms($grandTotal = null) + { + $amount = $grandTotal ?: $this->getTotal(); + return $this->gateway->getPaymentTermsForCountryAndValue($this->getCountry(), $amount); + } + + /** + * @return string + */ + abstract protected function getCountry(); + + public function canUseForCountry($country) + { + return $this->helper->transformCountryCodeToName($country) === $this->getCountry() + && parent::canUseForCountry($country); + } + + public function setupData() + { + parent::setupData(); + + $this->data->setGatewayFields(Mage::app()->getRequest()->getPost('payment')); + $this->data->setPaymentType('cc'); + $this->data->setInstalmentTerms( + $this->gateway->getPaymentTermsForCountryAndValue( + $this->helper->transformCountryCodeToName($this->data->getBillingAddress()->getCountry()), + $this->data->getAmountTotal() + ) + ); + $this->gatewayFields = $this->data->getGatewayFields(); + } + + public function transformPaymentData() + { + $this->paymentData = $this->adapter->transformCard($this->data); + } + + public function processPayment() + { + if ($this->gatewayFields['selected_card'] !== 'newcard') { + $customerId = $this->getOrder()->getCustomerId(); + $selectedCard = $this->gatewayFields['selected_card']; + $token = $this->gatewayFields['ebanx_token'][$selectedCard]; + + if (!Mage::getModel('ebanx/usercard')->doesCardBelongsToCustomer($token, $customerId)){ + $error = Mage::helper('ebanx/error'); + $country = $this->getOrder()->getBillingAddress()->getCountry(); + Mage::throwException($error->getError('GENERAL', $country)); + } + } + + parent::processPayment(); + } + + public function persistPayment() + { + parent::persistPayment(); + $selectedCard = $this->gatewayFields['selected_card']; + $last4 = substr($this->gatewayFields['ebanx_masked_card_number'][$selectedCard], -4); + $instalments = array_key_exists('instalments', $this->gatewayFields) ? $this->gatewayFields['instalments'] : 1; + $this->payment->setInstalments($instalments) + ->setCcLast4($last4) + ->setCcType($this->gatewayFields['ebanx_brand'][$selectedCard]); + + $this->persistCreditCardData(); + } + + private function persistCreditCardData() + { + if (!Mage::helper('ebanx')->saveCreditCardAllowed()) + { + return; + } + + $order = $this->getOrder(); + + if ($order->getCustomerIsGuest()) { + return; + } + + if (!isset($this->gatewayFields['ebanx_save_credit_card']) || $this->gatewayFields['ebanx_save_credit_card'] !== 'on') { + return; + } + + if ($this->gatewayFields['selected_card'] !== 'newcard') { + return; + } + + $customerId = $order->getCustomerId(); + $token = $this->gatewayFields['ebanx_token']['newcard']; + $brand = $this->gatewayFields['ebanx_brand']['newcard']; + $maskedCardNumber = $this->gatewayFields['ebanx_masked_card_number']['newcard']; + + if (!$customerId || !$token || !$brand || !$maskedCardNumber) { + return; + } + + $usercard = Mage::getModel('ebanx/usercard'); + if ($usercard->isCardAlreadySavedForCustomer($maskedCardNumber, $customerId)) { + return; + } + + Mage::getModel('ebanx/usercard')->setUserId($customerId) + ->setToken($token) + ->setMaskedNumber($maskedCardNumber) + ->setBrand($brand) + ->setPaymentMethod($this->getCode()) + ->save(); + } + + /** + * @return Mage_Sales_Model_Order + */ + private function getOrder() + { + $paymentInfo = $this->getInfoInstance(); + $orderId = $paymentInfo->getOrder()->getRealOrderId(); + + return Mage::getModel('sales/order')->loadByIncrementId($orderId); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Payment/Safetypay.php b/app/code/community/Ebanx/Gateway/Model/Payment/Safetypay.php new file mode 100644 index 00000000..907fe0fb --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Payment/Safetypay.php @@ -0,0 +1,29 @@ +gateway = $this->ebanx->safetyPayCash(); + } + + public function initialize( $paymentAction, $stateObject ) { + $safetyPayType = Mage::app()->getRequest()->getPost()['ebanx_safetypay_type']; + + $this->gateway = $this->ebanx->{'safetyPay' . $safetyPayType}(); + + parent::initialize( $paymentAction, $stateObject ); + } + + public function canUseForCountry($country) + { + return $this->helper->transformCountryCodeToName($country) === $this->getCountry() + && parent::canUseForCountry($country); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Peru/Pagoefectivo.php b/app/code/community/Ebanx/Gateway/Model/Peru/Pagoefectivo.php new file mode 100644 index 00000000..08df1eba --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Peru/Pagoefectivo.php @@ -0,0 +1,21 @@ +gateway = $this->ebanx->pagoefectivo(); + } + + public function isAvailable($quote = null) + { + return parent::isAvailable() && in_array($this->getCode(), explode(',', $this->configs['payment_methods_peru'])); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Peru/Safetypay.php b/app/code/community/Ebanx/Gateway/Model/Peru/Safetypay.php new file mode 100644 index 00000000..0899876d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Peru/Safetypay.php @@ -0,0 +1,24 @@ +getCode(), explode(',', $this->configs['payment_methods_peru'])); + } + + /** + * @return string + */ + protected function getCountry() + { + return Country::PERU; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Quote/Interest.php b/app/code/community/Ebanx/Gateway/Model/Quote/Interest.php new file mode 100644 index 00000000..f2c6f815 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Quote/Interest.php @@ -0,0 +1,64 @@ +setCode('ebanx_interest'); + } + + public function collect(Mage_Sales_Model_Quote_Address $address) + { + if ($address->getAddressType() !== Mage_Sales_Model_Quote_Address::TYPE_BILLING) { + return; + } + + $payment = $address->getQuote()->getPayment(); + + if (!$payment->hasMethodInstance() || Mage::app()->getRequest()->getActionName() !== 'savePayment') { + return; + } + + $isCardPayment = substr($payment->getMethodInstance()->getCode(), 0, 8) === 'ebanx_cc'; + if (!$isCardPayment) { + return; + } + + $paymentInstance = $payment->getMethodInstance(); + + $gatewayFields = Mage::app()->getRequest()->getPost('payment'); + if (!array_key_exists('instalments', $gatewayFields)) { + return; + } + $instalments = $gatewayFields['instalments']; + $grandTotal = $gatewayFields['grand_total']; + $instalmentTerms = $paymentInstance->getInstalmentTerms($grandTotal); + + if (!array_key_exists($instalments - 1, $instalmentTerms)) { + return; + } + + $grandTotal = $grandTotal ?: $instalmentTerms[0]->baseAmount; + $instalmentAmount = $instalmentTerms[$instalments - 1]->baseAmount; + $interestAmount = ($instalmentAmount * $instalments) - $grandTotal; + + if ($interestAmount > 0) { + $address->setEbanxInterestAmount($interestAmount); + $address->setGrandTotal($address->getGrandTotal() + $interestAmount); + } + } + + public function fetch(Mage_Sales_Model_Quote_Address $address) + { + $amount = $address->getEbanxInterestAmount(); + $title = Mage::helper('ebanx')->__('Interest Amount'); + if ($amount != 0) { + $address->addTotal(array( + 'code' => $this->getCode(), + 'title' => $title, + 'value' => $amount, + )); + } + return $this; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Quote/Localtax.php b/app/code/community/Ebanx/Gateway/Model/Quote/Localtax.php new file mode 100644 index 00000000..7be8d30e --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Quote/Localtax.php @@ -0,0 +1,53 @@ +setCode( 'ebanx_local_tax' ); + } + + public function collect(Mage_Sales_Model_Quote_Address $address) + { + if ($address->getAddressType() !== Mage_Sales_Model_Quote_Address::TYPE_BILLING) { + return; + } + + $isBrazilLocalAmount = Mage::app()->getStore()->getCurrentCurrencyCode() === 'BRL'; + if (!$isBrazilLocalAmount || Mage::getStoreConfig('payment/ebanx_settings/iof_local_amount') === '0') { + return; + } + + $payment = $address->getQuote()->getPayment(); + + if (!$payment->hasMethodInstance() || Mage::app()->getRequest()->getActionName() !== 'savePayment') { + return; + } + + $gatewayFields = Mage::app()->getRequest()->getPost('payment'); + $grandTotal = $gatewayFields['grand_total']; + + if ($grandTotal <= 0) { + return; + } + + $localTaxAmount = $grandTotal * 0.0038; + + $address->setEbanxLocalTaxAmount($localTaxAmount); + $address->setGrandTotal($address->getGrandTotal() + $localTaxAmount); + } + + public function fetch(Mage_Sales_Model_Quote_Address $address) + { + $amount = $address->getEbanxLocalTaxAmount(); + $title = Mage::helper( 'ebanx' )->__( 'IOF' ); + if ( $amount != 0 ) { + $address->addTotal( array( + 'code' => $this->getCode(), + 'title' => $title, + 'value' => $amount, + ) ); + } + return $this; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Resource/Lead.php b/app/code/community/Ebanx/Gateway/Model/Resource/Lead.php new file mode 100644 index 00000000..98032446 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Resource/Lead.php @@ -0,0 +1,9 @@ +_init('ebanx/lead', 'id'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Resource/Lead/Collection.php b/app/code/community/Ebanx/Gateway/Model/Resource/Lead/Collection.php new file mode 100644 index 00000000..5c0edd00 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Resource/Lead/Collection.php @@ -0,0 +1,9 @@ +_init('ebanx/lead'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Resource/Log.php b/app/code/community/Ebanx/Gateway/Model/Resource/Log.php new file mode 100644 index 00000000..65a41404 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Resource/Log.php @@ -0,0 +1,15 @@ +_init('ebanx/log', 'id'); + } + + public function truncate() { + $this->_getWriteAdapter()->query('TRUNCATE TABLE '.$this->getMainTable()); + + return $this; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Resource/Log/Collection.php b/app/code/community/Ebanx/Gateway/Model/Resource/Log/Collection.php new file mode 100644 index 00000000..5e90b4f7 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Resource/Log/Collection.php @@ -0,0 +1,9 @@ +_init('ebanx/log'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Resource/Usercard.php b/app/code/community/Ebanx/Gateway/Model/Resource/Usercard.php new file mode 100644 index 00000000..911dc044 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Resource/Usercard.php @@ -0,0 +1,9 @@ +_init('ebanx/usercard', 'ebanx_card_id'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Resource/Usercard/Collection.php b/app/code/community/Ebanx/Gateway/Model/Resource/Usercard/Collection.php new file mode 100644 index 00000000..fdea440d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Resource/Usercard/Collection.php @@ -0,0 +1,9 @@ +_init('ebanx/usercard'); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Customerfields.php b/app/code/community/Ebanx/Gateway/Model/Source/Customerfields.php new file mode 100644 index 00000000..72a5fc52 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Customerfields.php @@ -0,0 +1,24 @@ +getAttributesByEntityCode('customer'); + } + + private function getAttributesByEntityCode($entity) + { + $type = Mage::getSingleton('eav/config')->getEntityType($entity); + $attributes = Mage::getResourceModel('eav/entity_attribute_collection')->setEntityTypeFilter($type->getId())->getItems(); + $data = array(); + + foreach ($attributes as $attribute) { + if ($attribute->getIsVisible() && $attribute->getAttributeCode() !== '') { + $data[$attribute->getAttributeCode()] = $attribute->getFrontendLabel(); + } + } + + return $data; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Duedate.php b/app/code/community/Ebanx/Gateway/Model/Source/Duedate.php new file mode 100644 index 00000000..b7095b13 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Duedate.php @@ -0,0 +1,16 @@ + $i, 'label' => $i); + } + return $options; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Instalment.php b/app/code/community/Ebanx/Gateway/Model/Source/Instalment.php new file mode 100644 index 00000000..10451448 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Instalment.php @@ -0,0 +1,16 @@ + $i, 'label' => $i . 'x'); + } + return $options; + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Methodsargentina.php b/app/code/community/Ebanx/Gateway/Model/Source/Methodsargentina.php new file mode 100644 index 00000000..b0c8a69f --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Methodsargentina.php @@ -0,0 +1,31 @@ + self::CREDIT_CARD, + 'label' => Mage::helper('ebanx')->__('Credit Card') + ), + array( + 'value' => self::RAPIPAGO, + 'label' => Mage::helper('ebanx')->__('Rapipago') + ), + array( + 'value' => self::PAGOFACIL, + 'label' => Mage::helper('ebanx')->__('Pago Facil') + ), + array( + 'value' => self::OTROS_CUPONES, + 'label' => Mage::helper('ebanx')->__('Otros Cupones') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Methodsbrazil.php b/app/code/community/Ebanx/Gateway/Model/Source/Methodsbrazil.php new file mode 100644 index 00000000..fbc3e627 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Methodsbrazil.php @@ -0,0 +1,31 @@ + self::CREDIT_CARD, + 'label' => Mage::helper('ebanx')->__('Credit Card') + ), + array( + 'value' => self::BOLETO, + 'label' => Mage::helper('ebanx')->__('Boleto EBANX') + ), + array( + 'value' => self::TEF, + 'label' => Mage::helper('ebanx')->__('Online Banking (TEF)') + ), + array( + 'value' => self::WALLET, + 'label' => Mage::helper('ebanx')->__('EBANX Wallet') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Methodschile.php b/app/code/community/Ebanx/Gateway/Model/Source/Methodschile.php new file mode 100644 index 00000000..96a6f920 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Methodschile.php @@ -0,0 +1,31 @@ + self::SENCILLITO, + 'label' => Mage::helper('ebanx')->__('Sencillito') + ), + array( + 'value' => self::SERVIPAG, + 'label' => Mage::helper('ebanx')->__('Servipag') + ), + array( + 'value' => self::WEBPAY, + 'label' => Mage::helper('ebanx')->__('Webpay') + ), + array( + 'value' => self::MULTICAJA, + 'label' => Mage::helper('ebanx')->__('Multicaja') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Methodscolombia.php b/app/code/community/Ebanx/Gateway/Model/Source/Methodscolombia.php new file mode 100644 index 00000000..6e42d1b4 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Methodscolombia.php @@ -0,0 +1,26 @@ + self::EFT, + 'label' => Mage::helper('ebanx')->__('PSE - Pago Seguros en Líne (EFT)') + ), + array( + 'value' => self::BALOTO, + 'label' => Mage::helper('ebanx')->__('Baloto') + ), + array( + 'value' => self::CREDIT_CARD, + 'label' => Mage::helper('ebanx')->__('Credit Card') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Methodsecuador.php b/app/code/community/Ebanx/Gateway/Model/Source/Methodsecuador.php new file mode 100644 index 00000000..e031515c --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Methodsecuador.php @@ -0,0 +1,16 @@ + self::SAFETYPAY, + 'label' => Mage::helper('ebanx')->__('SafetyPay') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Methodsmexico.php b/app/code/community/Ebanx/Gateway/Model/Source/Methodsmexico.php new file mode 100644 index 00000000..fa83b9b3 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Methodsmexico.php @@ -0,0 +1,31 @@ + self::CREDIT_CARD, + 'label' => Mage::helper('ebanx')->__('Credit Card') + ), + array( + 'value' => self::DEBIT_CARD, + 'label' => Mage::helper('ebanx')->__('Debit Card') + ), + array( + 'value' => self::OXXO, + 'label' => Mage::helper('ebanx')->__('OXXO') + ), + array( + 'value' => self::SPEI, + 'label' => Mage::helper('ebanx')->__('SPEI') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Methodsperu.php b/app/code/community/Ebanx/Gateway/Model/Source/Methodsperu.php new file mode 100644 index 00000000..1c82dc7d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Methodsperu.php @@ -0,0 +1,21 @@ + self::SAFETYPAY, + 'label' => Mage::helper('ebanx')->__('SafetyPay') + ), + array( + 'value' => self::PAGOEFECTIVO, + 'label' => Mage::helper('ebanx')->__('PagoEfectivo') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Mode.php b/app/code/community/Ebanx/Gateway/Model/Source/Mode.php new file mode 100644 index 00000000..4e461af1 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Mode.php @@ -0,0 +1,21 @@ + self::SANDBOX, + 'label' => Mage::helper('ebanx')->__('Sandbox') + ), + array( + 'value' => self::LIVE, + 'label' => Mage::helper('ebanx')->__('Live') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Source/Persontype.php b/app/code/community/Ebanx/Gateway/Model/Source/Persontype.php new file mode 100644 index 00000000..de8bb090 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Source/Persontype.php @@ -0,0 +1,21 @@ + self::CPF, + 'label' => Mage::helper('ebanx')->__('CPF - Individuals') + ), + array( + 'value' => self::CNPJ, + 'label' => Mage::helper('ebanx')->__('CNPJ - Companies') + ), + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/Model/Usercard.php b/app/code/community/Ebanx/Gateway/Model/Usercard.php new file mode 100644 index 00000000..a5f5dc72 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Model/Usercard.php @@ -0,0 +1,120 @@ +_init('ebanx/usercard'); + } + + /** + * Remove the cards from array cards + * + * @param array $cards + * @param int $userId + * @return Varien_Object + */ + public function removeCardsFromUser($cards, $userId) + { + $cards = $this->getCollectionByCustomerIdAndCardId($userId, $cards); + + foreach ($cards as $card) { + $card->delete(); + } + } + + /** + * Returns the registry by user id and masked number + * + * @param int $userId + * @param string $maskedNumber + * @return Varien_Object + */ + public function getByCustomerIdAndMaskedNumber($userId, $maskedNumber) + { + return $this->getCollectionByCustomerIdAndMaskedNumber($userId, $maskedNumber)->getFirstItem(); + } + + /** + * Returns if a Card is already saved for the customer + * + * @param string $maskedNumber + * @param int $userId + * @return bool + */ + public function isCardAlreadySavedForCustomer($maskedNumber, $userId) + { + return $this->getCollectionByCustomerIdAndMaskedNumber($userId, $maskedNumber)->count() > 0; + } + + /** + * Returns if the Card belongs to the customer + * + * @param string $token + * @param int $userId + * @return bool + */ + public function doesCardBelongsToCustomer($token, $userId) + { + return $this->getCollectionByCustomerIdAndToken($userId, $token)->count() > 0; + } + + public function getCustomerSavedCards($userId) + { + return $this->getCollection() + ->addFieldToFilter('user_id', $userId); + } + + public function getPaymentMethodByToken($token) + { + $collection = $this->getCollection() + ->addFieldToFilter('token', $token); + + return $collection->getFirstItem()->getPaymentMethod(); + } + + /** + * Returns a collection by customer ID and Masked Number + * + * @param int $userId + * @param string $maskedNumber + * @return Ebanx_Gateway_Model_Resource_Usercard_Collection + */ + private function getCollectionByCustomerIdAndMaskedNumber($userId, $maskedNumber) + { + return $this->getCollection() + ->addFieldToFilter('user_id', $userId) + ->addFieldToFilter('masked_number', $maskedNumber); + } + + /** + * Returns a collection by customer ID and Card ID + * + * @param int $userId + * @param array $cardId + * @return Ebanx_Gateway_Model_Resource_Usercard_Collection + */ + private function getCollectionByCustomerIdAndCardId($userId, $cardId) + { + return $this->getCollection() + ->addFieldToFilter('user_id', $userId) + ->addFieldToFilter('ebanx_card_id', array('in' => $cardId)); + } + + /** + * Returns a collection by customer ID and Token + * + * @param int $userId + * @param string $token + * @return Ebanx_Gateway_Model_Resource_Usercard_Collection + */ + private function getCollectionByCustomerIdAndToken($userId, $token) + { + return $this->getCollection() + ->addFieldToFilter('user_id', $userId) + ->addFieldToFilter('token', $token); + } +} diff --git a/app/code/community/Ebanx/Gateway/Payment.php b/app/code/community/Ebanx/Gateway/Payment.php new file mode 100644 index 00000000..856d27e1 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/Payment.php @@ -0,0 +1,201 @@ +configs = Mage::getStoreConfig('payment/ebanx_settings'); + $this->ebanx = Mage::getSingleton('ebanx/api')->ebanx(); + $this->adapter = Mage::getModel('ebanx/adapters_paymentadapter'); + $this->helper = Mage::helper('ebanx'); + } + + public function initialize($paymentAction, $stateObject) + { + try { + $this->payment = $this->getInfoInstance(); + $this->order = $this->payment->getOrder(); + $this->customer = Mage::getModel('sales/order')->load($this->order->getId()); + + + $this->setupData(); + + $this->transformPaymentData(); + + $this->processPayment(); + + $this->persistPayment(); + + parent::initialize($paymentAction, $stateObject); + } catch (Exception $e) { + Mage::throwException($e->getMessage()); + } + } + + public function setupData() + { + // Create payment data + $id = $this->payment->getOrder()->getIncrementId(); + $time = time(); + $merchantPaymentCode = "$id-$time"; + + $this->data = new Varien_Object(); + $this->data + ->setMerchantPaymentCode($merchantPaymentCode) + ->setOrderId($id) + ->setDueDate($this->helper->getDueDate()) + ->setEbanxMethod($this->getCode()) + ->setStoreCurrency(Mage::app()->getStore() + ->getCurrentCurrencyCode()) + ->setAmountTotal($this->order->getGrandTotal()) + ->setPerson($this->customer) + ->setItems($this->order->getAllVisibleItems()) + ->setRemoteIp($this->order->getRemoteIp()) + ->setBillingAddress($this->order->getBillingAddress()) + ->setPayment($this->payment) + ->setOrder($this->order); + } + + public function transformPaymentData() + { + $this->paymentData = $this->adapter->transform($this->data); + } + + public function processPayment() + { + $res = $this->gateway->create($this->paymentData); + $error = Mage::helper('ebanx/error'); + + Ebanx_Gateway_Log_Logger_Checkout::persist(array( + 'config' => Mage::getSingleton('ebanx/api')->getConfig(), + 'request' => $this->paymentData, + 'response' => $res + )); + + if ($res['status'] !== 'SUCCESS') { + $country = $this->order->getBillingAddress()->getCountry(); + $code = $res['status_code']; + + $this->helper->errorLog($res); + Mage::throwException($error->getError($code, $country)." ($code)"); + } + + if ($res['payment']['status'] === 'CA') { + $country = $this->order->getBillingAddress()->getCountry(); + $errorType = 'GENERAL'; + + if (isset($res['payment']['transaction_status'])) { + $errorType = 'CC-'.$res['payment']['transaction_status']['code']; + } + + Mage::throwException($error->getError($errorType, $country)); + } + + $this->order->setEmailSent(true); + $this->order->sendNewOrderEmail(); + + // Set the URL for redirect + if (!empty($res['redirect_url'])) { + self::$redirect_url = $res['redirect_url']; + } else { + self::$redirect_url = Mage::getUrl('checkout/onepage/success'); + } + + $this->result = $res; + } + + public function persistPayment() + { + $this->payment + ->setEbanxPaymentHash($this->result['payment']['hash']) + ->setEbanxEnvironment($this->helper->getMode()) + ->setEbanxDueDate($this->helper->getDueDate($this->order->getCreatedAt())) + ->setEbanxLocalAmount($this->result['payment']['amount_br']); + + if ($this->order->getCustomerId()) { + $documentNumber = $this->helper->getDocumentNumber($this->order, $this->data); + $customer = Mage::getModel('customer/customer')->load($this->order->getCustomerId()) + ->setEbanxCustomerDocument($documentNumber); + + if ($this->order->getBillingAddress()->getCountry() === 'AR') { + $documentType = $this->helper->getDocumentType('AR'); + $customer->setEbanxCustomerDocumentType($documentType); + } + + $methodCode = $this->order->getPayment()->getMethodInstance()->getCode(); + $documentFields = $this->helper->getDocumentFieldsRequiredForMethod($methodCode); + foreach ($documentFields as $field) { + $fieldValue = Mage::getStoreConfig('payment/ebanx_settings/' . $field) ?: 'taxvat'; + $customer->setData($fieldValue,$documentNumber); + } + $customer->save(); + } + } + + public function refund(Varien_Object $payment, $amount) + { + $hash = $payment->getEbanxPaymentHash(); + $result = $this->ebanx->refund()->requestByHash($hash, $amount, $this->helper->__('Refund requested by Magento Admin Panel')); + + Ebanx_Gateway_Log_Logger_Refund::persist(array( + 'request' => array( + 'hash' => $hash, + 'amount' => $amount, + 'description' => $this->helper->__('Refund requested by Magento Admin Panel') + ), + 'response' => $result + )); + + if($result['status'] === 'ERROR') { + $errorMsg = $this->helper->__('Error processing refund: '.$result['status_message'].' ('.$result['status_code'].')'); + Mage::throwException($errorMsg); + } + return $this; + } + + public function getOrderPlaceRedirectUrl() + { + return self::$redirect_url; + } + + public function isAvailable($quote = null) + { + return Mage::getStoreConfig('payment/ebanx_settings/enabled') + && $this->helper->areKeysFilled(); + } + + public function canUseForCountry($country) + { + $countryName = $this->helper->transformCountryCodeToName($country); + + return $this->gateway->isAvailableForCountry($countryName); + } + + public function getTotal() + { + $quote = $this->getInfoInstance()->getQuote(); + if (!$quote) { + return $this->getInfoInstance()->getOrder()->getPayment()->getEbanxLocalAmount(); + } + return $quote->getGrandTotal(); + } +} diff --git a/app/code/community/Ebanx/Gateway/controllers/CustomerController.php b/app/code/community/Ebanx/Gateway/controllers/CustomerController.php new file mode 100644 index 00000000..c9994823 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/controllers/CustomerController.php @@ -0,0 +1,60 @@ +getLoginUrl(); + + if (!Mage::getSingleton('customer/session')->authenticate($this, $loginUrl)) { + $this->setFlag('', self::FLAG_NO_DISPATCH, true); + } + + return $this; + } + + /** + * Display list of customer's cards + * + * @return void + */ + public function usercardsAction() + { + $this->loadLayout(); + $this->_initLayoutMessages('customer/session'); + + $block = $this->getLayout()->getBlock('ebanx_customer_usercards_list'); + $block->setRefererUrl($this->_getRefererUrl()); + + $headBlock = $this->getLayout()->getBlock('head'); + $headBlock->setTitle(Mage::helper('ebanx')->__('My Payment Cards')); + + $this->renderLayout(); + } + + /** + * Remove card action + * + * @return void + */ + public function removecardAction() + { + $cardsToRemove = $this->getRequest()->getParam('card'); + + if (!empty($cardsToRemove)) { + Mage::getModel('ebanx/usercard')->removeCardsFromUser( + $cardsToRemove, + Mage::getSingleton('customer/session')->getCustomerId() + ); + Mage::getSingleton('customer/session')->addSuccess('The cards have been removed successfully.'); + } + + $this->_redirect('ebanx/customer/usercards'); + } +} diff --git a/app/code/community/Ebanx/Gateway/controllers/IndexController.php b/app/code/community/Ebanx/Gateway/controllers/IndexController.php new file mode 100644 index 00000000..dcfe1ab8 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/controllers/IndexController.php @@ -0,0 +1,18 @@ +getResponse()->setRedirect( + Mage::getUrl( + '*/payment/notify', + $this->getRequest()->getParams() + ), + 301 + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/controllers/LogController.php b/app/code/community/Ebanx/Gateway/controllers/LogController.php new file mode 100644 index 00000000..8a0aab29 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/controllers/LogController.php @@ -0,0 +1,24 @@ +getRequest()->getParam('integration_key'); + + if (empty($integration_key) || ($integration_key !== Mage::helper('ebanx/data')->getSandboxIntegrationKey() && $integration_key !== Mage::helper('ebanx/data')->getLiveIntegrationKey())) { + $this->norouteAction(); + + return; + } + + list($col, $res) = Ebanx_Gateway_Log_Logger::fetch($integration_key); + + Ebanx_Gateway_Log_Logger::delete($col); + + $this->getResponse() + ->setHeader('Content-Type', 'application/json') + ->setBody(json_encode($res)); + } +} diff --git a/app/code/community/Ebanx/Gateway/controllers/OneclickController.php b/app/code/community/Ebanx/Gateway/controllers/OneclickController.php new file mode 100644 index 00000000..450872aa --- /dev/null +++ b/app/code/community/Ebanx/Gateway/controllers/OneclickController.php @@ -0,0 +1,316 @@ +request = Mage::app()->getRequest()->getPost(); + } + + public function payAction() + { + if (!Mage::getSingleton('customer/session')->isLoggedIn() + || !isset($this->request['product'])) { + return $this->_redirect('/'); + } + $this->customer = Mage::getSingleton('customer/session')->getCustomer(); + $this->product = Mage::getModel('catalog/product')->load($this->request['product']); + + if (!$this->isCardFromCustomer()) { + return $this->_redirect('/'); + } + + $this->createOrder(array($this->request), $this->getPaymentMethod()); + + return $this->_redirect('sales/order/view', array('order_id' => $this->order->getId())); + } + + private function isCardFromCustomer() + { + $selectedCard = $this->request['payment']['selected_card']; + $cardToken = $this->request['payment']['ebanx_token'][$selectedCard]; + $customerId = $this->customer->getId(); + + return Mage::getModel('ebanx/usercard')->doesCardBelongsToCustomer($cardToken, $customerId); + } + + private function getPaymentMethod() + { + $selectedCard = $this->request['payment']['selected_card']; + $cardToken = $this->request['payment']['ebanx_token'][$selectedCard]; + + return Mage::getModel('ebanx/usercard')->getPaymentMethodByToken($cardToken); + } + + /** + * @param array $products + * @param string $paymentMethod + */ + private function createOrder($products, $paymentMethod) + { + $this->transaction = Mage::getModel('core/resource_transaction'); + $this->storeId = $this->customer->getStoreId(); + + $this->reservedOrderId = Mage::getSingleton('eav/config'); + $this->reservedOrderId = $this->reservedOrderId->getEntityType('order') + ->fetchNewIncrementId($this->storeId); + + $currencyCode = Mage::app()->getStore()->getCurrentCurrencyCode(); + $this->order = Mage::getModel('sales/order'); + $this->order = $this->order->setIncrementId($this->reservedOrderId) + ->setStoreId($this->storeId) + ->setQuoteId(0) + ->setGlobalCurrencyCode($currencyCode) + ->setBaseCurrencyCode($currencyCode) + ->setStoreCurrencyCode($currencyCode) + ->setOrderCurrencyCode($currencyCode); + + + $this->order->setCustomerEmail($this->customer->getEmail()) + ->setCustomerFirstname($this->customer->getFirstname()) + ->setCustomerLastname($this->customer->getLastname()) + ->setCustomerGroupId($this->customer->getGroupId()) + ->setCustomerIsGuest(0) + ->setCustomer($this->customer); + + + $billing = $this->customer->getDefaultBillingAddress(); + $billingAddress = Mage::getModel('sales/order_address') + ->setStoreId($this->storeId) + ->setAddressType(Mage_Sales_Model_Quote_Address::TYPE_BILLING) + ->setCustomerId($this->customer->getId()) + ->setCustomerAddressId($this->customer->getDefaultBilling()) + ->setCustomerAddress_id($billing->getEntityId()) + ->setPrefix($billing->getPrefix()) + ->setFirstname($billing->getFirstname()) + ->setMiddlename($billing->getMiddlename()) + ->setLastname($billing->getLastname()) + ->setSuffix($billing->getSuffix()) + ->setCompany($billing->getCompany()) + ->setStreet($billing->getStreet()) + ->setCity($billing->getCity()) + ->setCountry_id($billing->getCountryId()) + ->setRegion($billing->getRegion()) + ->setRegion_id($billing->getRegionId()) + ->setPostcode($billing->getPostcode()) + ->setTelephone($billing->getTelephone()) + ->setFax($billing->getFax()) + ->setVatId($this->customer->getEbanxCustomerDocument()); + $this->order->setBillingAddress($billingAddress); + + $shipping = $this->customer->getDefaultShippingAddress(); + $this->shippingAddress = Mage::getModel('sales/order_address'); + $this->shippingAddress = $this->shippingAddress->setStoreId($this->storeId) + ->setAddressType(Mage_Sales_Model_Quote_Address::TYPE_SHIPPING) + ->setCustomerId($this->customer->getId()) + ->setCustomerAddressId($this->customer->getDefaultShipping()) + ->setCustomer_address_id($shipping->getEntityId()) + ->setPrefix($shipping->getPrefix()) + ->setFirstname($shipping->getFirstname()) + ->setMiddlename($shipping->getMiddlename()) + ->setLastname($shipping->getLastname()) + ->setSuffix($shipping->getSuffix()) + ->setCompany($shipping->getCompany()) + ->setStreet($shipping->getStreet()) + ->setCity($shipping->getCity()) + ->setCountry_id($shipping->getCountryId()) + ->setRegion($shipping->getRegion()) + ->setRegion_id($shipping->getRegionId()) + ->setPostcode($shipping->getPostcode()) + ->setTelephone($shipping->getTelephone()) + ->setFax($shipping->getFax()) + ->setVatId($billing->getVatId()); + Mage::app()->getRequest()->setPost('ebanx-document', + array($paymentMethod => $this->customer->getEbanxCustomerDocument())); + $this->customer = $this->customer->setCountryId('br'); + + $this->order->setShippingAddress($this->shippingAddress) + ->setCustomerTaxvat($this->customer->getEbanxCustomerDocument()); + + $this->orderPayment = Mage::getModel('sales/order_payment'); + $this->orderPayment = $this->orderPayment->setStoreId($this->storeId) + ->setCustomerPaymentId(0) + ->setMethod($paymentMethod) + ->setPoNumber(' – '); + + $this->order->setPayment($this->orderPayment); + + $this->subTotal = 0; + + foreach ($products as $productRequest) { + $this->addProduct($productRequest); + } + + $this->order->setSubtotal($this->subTotal) + ->setBaseSubtotal($this->subTotal) + ->setGrandTotal($this->subTotal) + ->setBaseGrandTotal($this->subTotal); + + $this->transaction->addObject($this->order); + $this->transaction->addCommitCallback(array($this->order, 'place')); + $this->transaction->addCommitCallback(array($this->order, 'save')); + $this->transaction->save(); + } + + private function addProduct($requestData) + { + $request = new Varien_Object(); + $request->setData($requestData); + + $product = Mage::getModel('catalog/product')->load($request['product']); + + $cartCandidates = $product->getTypeInstance(true) + ->prepareForCartAdvanced($request, $product); + + if (is_string($cartCandidates)) { + throw new Exception($cartCandidates); + } + + if (!is_array($cartCandidates)) { + $cartCandidates = array($cartCandidates); + } + + $parentItem = null; + $errors = array(); + $items = array(); + foreach ($cartCandidates as $candidate) { + $item = $this->productToOrderItem($candidate, $candidate->getCartQty()); + + $items[] = $item; + + /** + * As parent item we should always use the item of first added product + */ + if (!$parentItem) { + $parentItem = $item; + } + if ($parentItem && $candidate->getParentProductId()) { + $item->setParentItem($parentItem); + } + /** + * We specify qty after we know about parent (for stock) + */ + $item->setQty($item->getQty() + $candidate->getCartQty()); + + // collect errors instead of throwing first one + if ($item->getHasError()) { + $message = $item->getMessage(); + if (!in_array($message, $errors)) { // filter duplicate messages + $errors[] = $message; + } + } + } + if (!empty($errors)) { + Mage::throwException(implode("\n", $errors)); + } + + foreach ($items as $item) { + $this->order->addItem($item); + } + + return $items; + } + + private function productToOrderItem(Mage_Catalog_Model_Product $product, $qty = 1) + { + $rowTotal = $product->getFinalPrice() * $qty; + + $options = $product->getCustomOptions(); + + $optionsByCode = array(); + + foreach ($options as $option) { + $quoteOption = Mage::getModel('sales/quote_item_option')->setData($option->getData()) + ->setProduct($option->getProduct()); + + $optionsByCode[$quoteOption->getCode()] = $quoteOption; + } + + $product->setCustomOptions($optionsByCode); + + $options = $product->getTypeInstance(true)->getOrderOptions($product); + + $orderItem = Mage::getModel('sales/order_item') + ->setStoreId($this->storeId) + ->setQuoteItemId(0) + ->setQuoteParentItemId(null) + ->setProductId($product->getId()) + ->setProductType($product->getTypeId()) + ->setQtyBackordered(null) + ->setTotalQtyOrdered($product['rqty']) + ->setQtyOrdered($product['qty']) + ->setName($product->getName()) + ->setSku($product->getSku()) + ->setPrice($product->getFinalPrice()) + ->setBasePrice($product->getFinalPrice()) + ->setOriginalPrice($product->getFinalPrice()) + ->setRowTotal($rowTotal) + ->setBaseRowTotal($rowTotal) + ->setWeeeTaxApplied(serialize(array())) + ->setBaseWeeeTaxDisposition(0) + ->setWeeeTaxDisposition(0) + ->setBaseWeeeTaxRowDisposition(0) + ->setWeeeTaxRowDisposition(0) + ->setBaseWeeeTaxAppliedAmount(0) + ->setBaseWeeeTaxAppliedRowAmount(0) + ->setWeeeTaxAppliedAmount(0) + ->setWeeeTaxAppliedRowAmount(0) + ->setProductOptions($options); + + $this->subTotal += $rowTotal; + + return $orderItem; + } +} diff --git a/app/code/community/Ebanx/Gateway/controllers/PaymentController.php b/app/code/community/Ebanx/Gateway/controllers/PaymentController.php new file mode 100644 index 00000000..ca2ff977 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/controllers/PaymentController.php @@ -0,0 +1,180 @@ + Mage_Sales_Model_Order::STATE_PROCESSING, + 'PE' => Mage_Sales_Model_Order::STATE_PENDING_PAYMENT, + 'CA' => Mage_Sales_Model_Order::STATE_CANCELED, + ); + + public function notifyAction() + { + try { + Ebanx_Gateway_Log_Logger_NotificationReceived::persist(array( + 'params' => $this->getRequest()->getParams() + )); + + $this->initialize(); + } catch (Ebanx_Gateway_Exception $e) { + $this->helper->errorLog($e->getMessage()); + + return $this->setResponseToJson(array( + 'success' => false, + 'message' => $e->getMessage() + )); + } + + try { + $this->updateOrder($this->statusEbanx); + + if (Mage::helper('ebanx')->isEbanxMethod($this->_getPaymentMethod($this->order)) + && Mage::getStoreConfig('payment/ebanx_settings/create_invoice') + && strtoupper($this->statusEbanx) === 'CO' + && $this->order->canInvoice()) { + $this->createInvoice(); + } + + $this->setResponseToJson(array( + 'success' => true, + 'order' => $this->order->getIncrementId() + )); + } catch (Exception $e) { + $this->order->addStatusHistoryComment($this->helper->__('EBANX: We could not update the order status. Error message: %s.', $e->getMessage())); + $this->helper->errorLog($e->getMessage()); + Mage::throwException(get_class($e).': '.$e->getMessage()); + } + } + + private function loadOrder() + { + try { + $this->order = $this->helper->getOrderByHash($this->hash); + } catch (Exception $e) { + // LEGACY: Support for legacy orders which store their hash somewhere else + $this->order = $this->helper->getLegacyOrderByHash($this->hash); + } + } + + private function initialize() + { + $this->helper = Mage::helper('ebanx/order'); + $this->validateEbanxPaymentRequest(); + $this->hash = $this->getRequest()->getParam('hash_codes'); + $this->loadOrder(); + $this->statusEbanx = $this->loadEbanxPaymentStatus(); + $this->validateStatus(); + } + + private function validateEbanxPaymentRequest() + { + $request = $this->getRequest(); + $operation = $request->getParam('operation'); + $notification_type = $request->getParam('notification_type'); + $hash_codes = $request->getParam('hash_codes'); + + if (empty($operation)) { + throw new Ebanx_Gateway_Exception($this->helper->__('EBANX: Invalid operation parameter.')); + } + + if (empty($notification_type)) { + throw new Ebanx_Gateway_Exception($this->helper->__('EBANX: Invalid notification type parameter.')); + } + + if (empty($hash_codes)) { + throw new Ebanx_Gateway_Exception($this->helper->__('EBANX: Invalid hash parameter.')); + } + } + + private function _getPaymentMethod($order) + { + return $order->getPayment()->getMethodInstance()->getCode(); + } + + private function setResponseToJson($data) + { + $this->getResponse()->clearHeaders()->setHeader( + 'Content-type', + 'application/json' + ); + + $this->getResponse()->setBody( + Mage::helper('core')->jsonEncode($data) + ); + } + + private function loadEbanxPaymentStatus() + { + $api = Mage::getSingleton('ebanx/api')->ebanx(); + $isSandbox = $this->loadOrderEnv() === 'sandbox'; + $payment = $api->paymentInfo()->findByHash($this->hash, $isSandbox); + + Ebanx_Gateway_Log_Logger_NotificationQuery::persist(array( + 'params' => $this->getRequest()->getParams(), + 'payment' => $payment, + 'isSandbox' => $isSandbox + )); + + $this->helper->log($payment, 'ebanx_payment_notification'); + + if ($payment['status'] !== 'SUCCESS') { + throw new Ebanx_Gateway_Exception($this->helper->__('EBANX: Payment doesn\'t exist. ' . ($isSandbox ? 'sand' : 'live'))); + } + + return $payment['payment']['status']; + } + + private function loadOrderEnv() + { + $env = $this->order->getPayment()->getEbanxEnvironment(); + + // LEGACY: Fallback in case of legacy orders that don't save environment + if (!$env) { + $env = Mage::getSingleton('ebanx/api')->getConfig()->isSandbox ? 'sandbox' : 'live'; + } + + return $env; + } + + // Actions + + private function updateOrder($statusEbanx) + { + $statusMagento = $this->helper->getEbanxMagentoOrder($statusEbanx); + + $this->order->setData('status', $statusMagento); + $this->order->setState($this->ebanxStatusToState[$statusEbanx]); + $this->order->addStatusHistoryComment($this->helper->__('EBANX: The payment has been updated to: %s.', $this->helper->getTranslatedOrderStatus($statusEbanx))); + $this->order->save(); + } + + private function createInvoice() { + + $invoice = $this->order->prepareInvoice(); + $invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE); + $invoice->setTransactionId($this->order->getPayment()->getEbanxPaymentHash()); + $invoice->register()->pay(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + } + + private function validateStatus() { + if (array_key_exists($this->statusEbanx, $this->ebanxStatusToState)) { + return; + } + + throw new Ebanx_Gateway_Exception( + $this->helper->__( + 'EBANX: Invalid payment status: %s.', + $this->statusEbanx + ) + ); + } +} diff --git a/app/code/community/Ebanx/Gateway/controllers/VoucherController.php b/app/code/community/Ebanx/Gateway/controllers/VoucherController.php new file mode 100644 index 00000000..20a16843 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/controllers/VoucherController.php @@ -0,0 +1,27 @@ +getRequest()->getParam('hash'); + + $url = Mage::helper('ebanx')->getVoucherUrlByHash($hash); + + if (!in_array('curl', get_loaded_extensions())) { + $this->getResponse() + ->setHeader('Content-Type', 'text/html') + ->setBody(file_get_contents($url)); + } + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + $html = curl_exec($curl); + if (curl_error($curl)) { + return; + } + curl_close($curl); + $this->getResponse() + ->setHeader('Content-Type', 'text/html') + ->setBody($html); + } +} diff --git a/app/code/community/Ebanx/Gateway/etc/config.xml b/app/code/community/Ebanx/Gateway/etc/config.xml new file mode 100644 index 00000000..e88719b8 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/etc/config.xml @@ -0,0 +1,370 @@ + + + + + 2.7.0 + + + + + + Ebanx_Gateway_Helper + + + + + + + + EBANX + Ebanx_Gateway_Model_Import + + + + + + + + Ebanx_Gateway_Block + + + + + + Ebanx_Gateway_Model + ebanx_resource + + + Ebanx_Gateway_Model_Resource + + + ebanx_user_cards
+
+ + ebanx_store_lead
+
+ + ebanx_logs
+
+
+
+
+ + + + + + singleton + Ebanx_Gateway_Model_Capture + capture_payment + + + + + + + singleton + Ebanx_Gateway_Model_Observer + observeConfigSection + + + + + + + singleton + Ebanx_Gateway_Model_Observer + observeAdvancedSection + + + + + + + + + Ebanx_Gateway + Mage_Sales_Model_Resource_Setup + + + + + + + * + + + * + + + * + + + + + * + + + + + + + + + ebanx/quote_interest + discount + tax + + + ebanx/quote_localtax + discount + tax + + + + +
+ + + + standard + + Ebanx_Gateway + ebanx + + + + + + + ebanx.xml + + + + + + + + + ebanx.xml + + + + + + + + + 1 + ebanx/brazil_boleto + Boleto Bancário + sale + 1 + + + 1 + ebanx/brazil_tef + Débito Online + sale + 1 + + + 1 + ebanx/brazil_creditcard + Cartão de Crédito + sale + 1 + + + 1 + ebanx/brazil_wallet + Saldo EBANX + sale + 1 + + + + 1 + ebanx/colombia_baloto + Baloto + sale + 1 + + + 1 + ebanx/colombia_creditcard + Tarjeta de Credito + sale + 1 + + + 1 + ebanx/colombia_pse + PSE - Pago Seguros en Línea + sale + 1 + + + + 1 + ebanx/chile_sencillito + Sencillito + sale + 1 + + + 1 + ebanx/chile_servipag + Servipag + sale + 1 + + + 1 + ebanx/chile_webpay + Webpay + sale + 1 + + + 1 + ebanx/chile_multicaja + Multicaja + sale + 1 + + + + 1 + ebanx/peru_pagoefectivo + PagoEfectivo + sale + 1 + + + 1 + ebanx/peru_safetypay + SafetyPay + sale + 1 + + + + 1 + ebanx/mexico_oxxo + OXXO + sale + 1 + + + 1 + ebanx/mexico_spei + SPEI + sale + 1 + + + 1 + ebanx/mexico_creditcard + Tarjeta de Credito + sale + 1 + + + 1 + ebanx/mexico_debitcard + Tarjeta de Debito + sale + 1 + + + + 1 + ebanx/argentina_creditcard + Tarjeta de Credito + sale + 1 + + + 1 + ebanx/argentina_rapipago + Rapipago + sale + 1 + + + 1 + ebanx/argentina_pagofacil + Pago Facil + sale + 1 + + + 1 + ebanx/argentina_otroscupones + Otros Cupones + sale + 2 + + + + 1 + ebanx/ecuador_safetypay + SafetyPay + sale + 1 + + + + cpf + 1 + 1 + + ebanx_cc_br,ebanx_boleto,ebanx_tef,ebanx_wallet + ebanx_sencillito,ebanx_servipag,ebanx_webpay + ebanx_sencillito,ebanx_servipag,ebanx_multicaja,ebanx_webpay + ebanx_baloto,ebanx_pse,ebanx_cc_co + ebanx_pagoefectivo,ebanx_safetypay + ebanx_oxxo,ebanx_spei,ebanx_cc_mx,ebanx_dc_mx + ebanx_cc_ar,ebanx_rapipago,ebanx_pagofacil,ebanx_otroscupones + ebanx_safetypay_ec + + 1 + 1 + 0 + 0 + 1 + 1 + + processing + holded + pending + canceled + + 0 + + + + + + + + + + + 0 + ebanx/legacy_standard + Ebanx Standard + sale + 2 + + + 0 + ebanx/legacy_express + Ebanx Express + sale + 2 + + + +
diff --git a/app/code/community/Ebanx/Gateway/etc/system.xml b/app/code/community/Ebanx/Gateway/etc/system.xml new file mode 100644 index 00000000..5ca6ed73 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/etc/system.xml @@ -0,0 +1,498 @@ + + + + + + + + 0 + 1 + 1 + 1 + EBANX
+ +
+

EBANX easy-to-setup checkout allows your business to accept local payments in Brazil, Argentina, Ecuador, Mexico, Colombia, Chile & Peru.

+

You can obtain your API keys in the settings section, on the EBANX Dashboard.

+ ]]>
+ + + + select + adminhtml/system_config_source_yesno + 0 + 1 + 1 + + + + + adminhtml/system_config_form_field_heading + 5 + 1 + 1 + + + + select + ebanx/source_mode + 10 + 1 + 1 + 0 + + + + text + 20 + 1 + 1 + 0 + + + + text + 25 + 1 + 1 + 0 + + + + text + 30 + 1 + 1 + 0 + + + + text + 35 + 1 + 1 + 0 + + + + + adminhtml/system_config_form_field_heading + 40 + 1 + 1 + + + + Record all errors that occur when executing a transaction. + select + adminhtml/system_config_source_yesno + 50 + 1 + 1 + 0 + + + + + Create an invoice when payment is confirmed + select + adminhtml/system_config_source_yesno + 55 + 1 + 1 + 0 + + + + + In order to process with the EBANX Plugin in Brazil there a few mandatory fields + such as CPF identification for individuals and CNPJ for companies. + + multiselect + ebanx/source_persontype + required-entry + 60 + 1 + 1 + 0 + + + + + Get in touch with the EBANX team to validate the change and then, you just have to select "no" on this option. + That way, your customer will not be charged with IOF (Brazilian Tax on Financial Operations). + + select + adminhtml/system_config_source_yesno + 65 + 1 + 1 + 0 + + + + + adminhtml/system_config_form_field_heading + 100 + 1 + 1 + + + + multiselect + ebanx/source_methodsbrazil + 110 + 1 + 1 + 0 + 1 + + + + multiselect + ebanx/source_methodsmexico + 120 + 1 + 1 + 0 + 1 + + + + multiselect + ebanx/source_methodschile + 130 + 1 + 1 + 0 + 1 + + + + multiselect + ebanx/source_methodscolombia + 140 + 1 + 1 + 0 + 1 + + + + multiselect + ebanx/source_methodsperu + 150 + 1 + 1 + 0 + 1 + + + + multiselect + ebanx/source_methodsargentina + 160 + 1 + 1 + 0 + 1 + + + + multiselect + ebanx/source_methodsecuador + 165 + 1 + 1 + 0 + 1 + + + + + adminhtml/system_config_form_field_heading + 170 + 1 + 1 + + + + + Selecting "Yes", you will always show payment method logos helping your customer to identify which suppliers are accepted. Otherwise, if you select "No" the customer will see only the logo of the payment method chosen. + select + adminhtml/system_config_source_yesno + 180 + 1 + 1 + 0 + + + + + adminhtml/system_config_form_field_heading + 200 + 1 + 1 + + + + + Allow your customer to save credit card and debit card data for future purchases. + + select + adminhtml/system_config_source_yesno + 220 + 1 + 1 + 0 + + + + Allow your customer to complete payments in one-click using credit cards saved. + + select + adminhtml/system_config_source_yesno + 225 + 1 + 1 + 0 + + + + Automatically capture payments from your customers, just for credit card. Otherwise + you will need to capture the payment manually by creating an invoice for each order. Not captured payments will be cancelled + in 4 days. + + select + adminhtml/system_config_source_yesno + 230 + 1 + 1 + 0 + --> + + + Establish the maximum number of instalments in which your customer can pay, as consented on your contract. + select + ebanx/source_instalment + 235 + 1 + 1 + 0 + + + + Set the minimum installment value to show to the options for your customer on the checkout page. The default values are Brazil: BRL 5, Mexico: MXN 100, Colombia: COL 1, Argentina: ARS 1. Any amount under these will not be considered. + text + required-entry validate-number + 240 + 1 + 1 + 0 + + + + Enable and set custom interest rates according to the number of installments you have allowed the payment for your customers. + select + adminhtml/system_config_source_yesno + 245 + 1 + 1 + 0 + + + + ebanx/adminhtml_system_config_form_field_interest + adminhtml/system_config_backend_serialized_array + 250 + 1 + 1 + 0 + + 1 + + + + + + adminhtml/system_config_form_field_heading + 300 + 1 + 1 + + + + Define the maximum number of days on which your customer can complete the payment of: Boleto, OXXO, Sencillito, PagoEfectivo and SafetyPay. + select + ebanx/source_duedate + 310 + 1 + 1 + 0 + + + + + + adminhtml/system_config_form_field_heading + 320 + 1 + 1 + + + + select + adminhtml/system_config_source_order_status + Payment paid, the customer has paid the order and EBANX received the payment. + 330 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status + Payment that have not yet been paid or is still being processed. + 340 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status + Payment was created but it has not yet been assigned to a payment method. + 350 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status + Payment canceled when the customer hasn't make the payment, so EBANX set as + canceled. + + 360 + 1 + 1 + 0 + + + + + + adminhtml/system_config_form_field_heading + 400 + 1 + 1 + + + + When enabled will morph taxvat field label and mask to match the country's document. + select + adminhtml/system_config_source_yesno + 404 + 1 + 1 + + + + text + 409 + 1 + 1 + 0 + + + + text + 410 + 1 + 1 + 0 + + + + text + 411 + 1 + 1 + 0 + + + + text + 412 + 1 + 1 + 0 + + + + text + 413 + 1 + 1 + 0 + + + + text + 414 + 1 + 1 + 0 + + + + text + 415 + Processing with EBANX requires a few mandatory fields that need to be included in your checkout when processing to Brazil, Chile, Colombia, Peru and Argentina, such as: CPF, CNPJ, DNI and RUT. You can choose to: replace your TAX/VAT fields and name it as these mandatories field or add your own field. + 1 + 1 + 0 + + + + adminhtml/system_config_form_field_heading + 420 + 1 + 1 + + + + text + 421 + EBANX uses the number typed on Address field as street number. If you use a custom field for it you can type its name here. + 1 + 1 + 0 + + +
+
+
+
+
diff --git a/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-install-2.0.1.php b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-install-2.0.1.php new file mode 100644 index 00000000..dd7e14a6 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-install-2.0.1.php @@ -0,0 +1,104 @@ +startSetup(); + +$installer->addAttribute('order_payment', 'ebanx_payment_hash', array( + 'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR +, 'backend_type' => 'text' +, 'frontend_input' => 'text' +, 'is_user_defined' => 0 +, 'label' => 'EBANX Hash' +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'searchable' => 1 +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + +$installer->addAttribute('order_payment', 'ebanx_due_date', array( + 'type' => Varien_Db_Ddl_Table::TYPE_DATETIME +, 'is_user_defined' => 0 +, 'label' => 'Due Date' +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'nullable' => true +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + +$installer->addAttribute('order_payment', 'ebanx_bar_code', array( + 'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR +, 'is_user_defined' => 0 +, 'label' => 'Bar Code' +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'nullable' => true +, 'searchable' => 0 +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + +$installer->addAttribute('order_payment', 'ebanx_environment', array( + 'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR +, 'is_user_defined' => 0 +, 'label' => 'Environment' +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'nullable' => true +, 'searchable' => 0 +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + +$installer->addAttribute('order_payment', 'ebanx_instalments', array( + 'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR +, 'is_user_defined' => 0 +, 'label' => 'Instalments' +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'nullable' => true +, 'searchable' => 0 +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + +$installer->addAttribute('order_payment', 'ebanx_local_amount', array( + 'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR +, 'is_user_defined' => 0 +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'nullable' => true +, 'searchable' => 0 +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + +$installer->addAttribute('customer', 'ebanx_customer_document', array( + 'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR +, 'is_user_defined' => 0 +, 'label' => 'Customer Document' +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'nullable' => true +, 'searchable' => 0 +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + + +$installer->endSetup(); diff --git a/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.0.1-2.0.2.php b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.0.1-2.0.2.php new file mode 100644 index 00000000..d159b850 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.0.1-2.0.2.php @@ -0,0 +1,26 @@ +startSetup(); + +if (!$installer->tableExists('ebanx_user_cards')) { + + $installer->run(" + CREATE TABLE {$this->getTable('ebanx_user_cards')} ( + `ebanx_card_id` int(11) unsigned NOT NULL auto_increment, + `user_id` int(11) NOT NULL default 0, + `token` varchar(255) NOT NULL default '', + `masked_number` varchar(20) NOT NULL default '', + `brand` varchar(255) NOT NULL default '', + `payment_method` varchar(255) NOT NULL default '', + PRIMARY KEY (ebanx_card_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +"); +} + + +$installer->endSetup(); diff --git a/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.6.0-2.6.1.php b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.6.0-2.6.1.php new file mode 100644 index 00000000..f975839d --- /dev/null +++ b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.6.0-2.6.1.php @@ -0,0 +1,33 @@ +startSetup(); + +if (!$installer->tableExists('ebanx_logs')) { + $installer->run(" + CREATE TABLE {$this->getTable('ebanx_logs')} ( + id int NOT NULL AUTO_INCREMENT, + time datetime(6) NOT NULL DEFAULT NOW(6), + event varchar(150) NOT NULL, + integration_key VARCHAR(255) NOT NULL, + log blob NOT NULL, + UNIQUE KEY id (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + "); +} + +if (!$installer->tableExists('ebanx_store_lead')) { + $installer->run(" + CREATE TABLE {$this->getTable('ebanx_store_lead')} ( + id int NOT NULL AUTO_INCREMENT, + id_store int NOT NULL, + id_lead varchar(150) NOT NULL, + UNIQUE KEY id (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + "); +} + +$installer->endSetup(); diff --git a/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.6.1-2.6.2.php b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.6.1-2.6.2.php new file mode 100644 index 00000000..aad21bf8 --- /dev/null +++ b/app/code/community/Ebanx/Gateway/sql/ebanx_magento_setup/mysql4-upgrade-2.6.1-2.6.2.php @@ -0,0 +1,63 @@ +startSetup(); + +$entityTypeId = $installer->getEntityTypeId('customer_address'); +$attributeSetId = $installer->getDefaultAttributeSetId($entityTypeId); +$attributeGroupId = $installer->getDefaultAttributeGroupId($entityTypeId, $attributeSetId); + +$installer->addAttribute('customer', 'ebanx_customer_document_type', array( + 'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR +, 'is_user_defined' => 0 +, 'label' => 'Customer Document Type' +, 'visible' => 1 +, 'required' => 0 +, 'user_defined' => 0 +, 'nullable' => true +, 'searchable' => 0 +, 'filterable' => 0 +, 'comparable' => 0 +, 'default' => null +)); + +$installer->addAttribute("customer_address", "ebanx_document_type", array( + "type" => "varchar", + "backend" => "", + "label" => "Delivery Instruction", + "input" => "text", + "source" => "", + "visible" => true, + "required" => false, + "default" => "", + "frontend" => "", + "unique" => false, + "note" => "Custom Attribute Will Be Used Save Customer Document Type" +)); + +$attribute = Mage::getSingleton("eav/config")->getAttribute("customer_address", "ebanx_document_type"); + +$installer->addAttributeToGroup( + $entityTypeId, $attributeSetId, $attributeGroupId, 'ebanx_document_type', '999' //sort_order +); + +$used_in_forms = array(); + +$used_in_forms[]="adminhtml_customer"; +$used_in_forms[]="checkout_register"; +$used_in_forms[]="customer_account_create"; +$used_in_forms[] = "customer_address_edit"; //this form code is used in checkout billing/shipping address +$used_in_forms[]="adminhtml_checkout"; +$attribute->setData("used_in_forms", $used_in_forms) + ->setData("is_used_for_customer_segment", true) + ->setData("is_system", 0) + ->setData("is_user_defined", 1) + ->setData("is_visible", 1) + ->setData("sort_order", 100) +; +$attribute->save(); + +$installer->endSetup(); diff --git a/app/code/local/Martin/Recommend/Block/Adminhtml/recommend.php b/app/code/local/Martin/Recommend/Block/Adminhtml/Recommend.php similarity index 100% rename from app/code/local/Martin/Recommend/Block/Adminhtml/recommend.php rename to app/code/local/Martin/Recommend/Block/Adminhtml/Recommend.php diff --git a/app/design/adminhtml/default/default/layout/ebanx.xml b/app/design/adminhtml/default/default/layout/ebanx.xml new file mode 100644 index 00000000..4a77e3f9 --- /dev/null +++ b/app/design/adminhtml/default/default/layout/ebanx.xml @@ -0,0 +1,24 @@ + + + + + + ebanx/chosen/chosen.proto.min.js + + + skin_js + ebanx/js/chosen-ebanx.js + + + ebanx/css/chosen.css + + + ebanx/css/chosen.overwrite.css + + + skin_js + ebanx/js/iof-option.js + + + + diff --git a/app/design/adminhtml/default/default/template/ebanx/info/default.phtml b/app/design/adminhtml/default/default/template/ebanx/info/default.phtml new file mode 100644 index 00000000..6726528e --- /dev/null +++ b/app/design/adminhtml/default/default/template/ebanx/info/default.phtml @@ -0,0 +1,63 @@ +getInfo()->getEbanxHash()); + + $hash = $isLegacy + ? $this->getInfo()->getEbanxHash() + : $this->getInfo()->getEbanxPaymentHash(); + + $orderState = $this->getInfo()->getOrder()->getState(); + $notificationUrl = $this->getNotificationUrl($hash); +?> + + + + + + + + + + + + + + + + + + + + + + +
__('EBANX Dashboard') ?>__('Open Payment Page') ?>
__('EBANX Payment Hash') ?>
__('EBANX Payment Method') ?>getInfo()->getMethodInstance()->getTitle() ?>
__('EBANX Force Update Check') ?> + +
+
diff --git a/app/design/frontend/base/default/layout/ebanx.xml b/app/design/frontend/base/default/layout/ebanx.xml new file mode 100644 index 00000000..7fcc8ad7 --- /dev/null +++ b/app/design/frontend/base/default/layout/ebanx.xml @@ -0,0 +1,226 @@ + + + + + + + + ebanx/css/checkout.css + + + ebanx/css/sandbox-alert.css + + + + + + ]]> + + + + + + + + skin_js + + + + skin_js + + + + skin_js + ebanx/js/checkout.js + + + skin_js + ebanx/js/document.js + + + skin_js + ebanx/js/validator.js + + + + + + + + + + skin_js + ebanx/js/firecheckout-interest.js + + + + + + + skin_js + ebanx/js/onestepcheckout.js + + + + + + + skin_js + ebanx/js/amscheckout-inputs.js + + + ebanx/css/amscheckout.css + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ebanx/css/thankyou-page.css + + + + + + skin_js + ebanx/js/success.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ebanx/css/oneclick.css + + + skin_js + ebanx/js/oneclick.js + + + + + + + + + + + + + + + skin_js + + + + + + + + + + + customer_usercards + ebanx/customer/usercards + + + + + + + + + + + + + Credit Cards list + + + + + + + + + + diff --git a/app/design/frontend/base/default/template/ebanx/catalog/product/view/oneclick.phtml b/app/design/frontend/base/default/template/ebanx/catalog/product/view/oneclick.phtml new file mode 100644 index 00000000..84025d55 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/catalog/product/view/oneclick.phtml @@ -0,0 +1,33 @@ +getLocalCurrency(); +$savedCards = $this->usercards; +$text = $this->getText(); +$lowerCountry = strtolower($this->getCountry()); +?> +canShowOneclickButton()): ?> +
+ Pague com um clique +
+ +

Endereço de entrega:

+

getAddress()['street'] ?>

+ +

Escolha o cartão:

+ +
+
+ diff --git a/app/design/frontend/base/default/template/ebanx/checkout/checkout.phtml b/app/design/frontend/base/default/template/ebanx/checkout/checkout.phtml new file mode 100644 index 00000000..6e5974ca --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/checkout.phtml @@ -0,0 +1,14 @@ + + + + diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success.phtml new file mode 100644 index 00000000..bf74e861 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success.phtml @@ -0,0 +1,5 @@ +getSuccessPaymentBlock(); + +echo $this->getChildHtml($paymentBlock); diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/apps.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/apps.phtml new file mode 100644 index 00000000..4eff58d5 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/apps.phtml @@ -0,0 +1,22 @@ + + +
+

Baixe o app para IOS ou Android da Conta EBANX.

+ + +
+
+ + + +
+
+ + + +
+
+ +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/baloto.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/baloto.phtml new file mode 100644 index 00000000..1cb4d1a9 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/baloto.phtml @@ -0,0 +1,25 @@ +getOrder(); +?> +
+

getBillingAddress()->getFirstname()) ?>, su boleta Baloto fue generado con éxito.

+

Una copia de la boleta fue enviada al correo electrónico getBillingAddress()->getEmail() ?>.

+

Si tienes dudas, por favor escribe a soporte@ebanx.com.

+ +
+ + +
+
+ +
+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/boleto.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/boleto.phtml new file mode 100644 index 00000000..b4eee52c --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/boleto.phtml @@ -0,0 +1,69 @@ +getOrder(); +$payment = $this->getPayment(); +$dueDate = $this->helper->getDueDate($order->getEbanxDueDate(), 'dd/MM'); +$is_mobile = Zend_Http_UserAgent_Mobile::match(Mage::helper('core/http')->getHttpUserAgent(), $_SERVER); +?> +
+

getBillingAddress()->getFirstname(); ?>, seu boleto foi gerado e a data de vencimento é dia + getEbanxDueDate() ?> :)

+

Enviamos uma cópia para o email getBillingAddress()->getEmail(); ?>

+

Pague o boleto no Internet Banking de seu banco apenas copiando o código de barras! Você também pode imprimir o + boleto e pagar em casas lotéricas e no caixa do seu banco.

+

Dica: Pagar seu boleto até às 21h de dias úteis, faz com que o pagamento tenha a chance de ser confirmado + mais rápido :)

+ +

+ getEbanxBarCodeFormated(); + echo $barCodeFormated['boleto1'] . '.' . $barCodeFormated['boleto2'] . ' ' . $barCodeFormated['boleto3'] . '.' . $barCodeFormated['boleto4'] . ' ' . $barCodeFormated['boleto5'] . '.' . $barCodeFormated['boleto6'] . ' ' . $barCodeFormated['boleto7'] . ' ' . $barCodeFormated['boleto8']; + ?> +

+ +
+ +
+ + +
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardar.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardar.phtml new file mode 100644 index 00000000..420f3a03 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardar.phtml @@ -0,0 +1,13 @@ +getOrder(); +$payment = $this->getPayment(); +?> +
+

getBillingAddress()->getFirstname()) ?>, su pago de + formatPriceWithLocalCurrency($this->currencyCode, $payment->getEbanxLocalAmount()); ?> + fue aprobado./

+

Si tiene alguna duda acerca de su pago, acceda a la cuenta EBANX con el correo electrónico + getBillingAddress()->getEmail() ?>.

+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardbr.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardbr.phtml new file mode 100644 index 00000000..33af4376 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardbr.phtml @@ -0,0 +1,13 @@ +getOrder(); +$payment = $this->getPayment(); +?> +
+

getBillingAddress()->getFirstname()) ?>, seu pagamento de + formatPriceWithLocalCurrency($this->currencyCode, $payment->getEbanxLocalAmount()); ?> + foi aprovado o/

+

Se tiver alguma dúvida em relação ao seu pagamento, acesse a Conta EBANX com o email + getBillingAddress()->getEmail() ?>.

+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardco.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardco.phtml new file mode 100644 index 00000000..5eeeb6b3 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardco.phtml @@ -0,0 +1,13 @@ +getOrder(); +$payment = $this->getPayment(); +?> +
+

getBillingAddress()->getFirstname()) ?>, su pago de + formatPriceWithLocalCurrency($this->currencyCode, $payment->getEbanxLocalAmount()); ?> + fue aprobado./

+

Si tiene alguna duda acerca de su pago, acceda a la cuenta EBANX con el correo electrónico + getBillingAddress()->getEmail() ?>.

+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardmx.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardmx.phtml new file mode 100644 index 00000000..929169b6 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/creditcardmx.phtml @@ -0,0 +1,13 @@ +getOrder(); +$payment = $this->getPayment(); +?> +
+

getBillingAddress()->getFirstname()) ?>, su pago de + formatPriceWithLocalCurrency($this->currencyCode, $payment->getEbanxLocalAmount()); ?> + fue aprobado o/

+

Si tiene alguna duda acerca de su pago, acceda a la cuenta EBANX con el correo electrónico + getBillingAddress()->getEmail() ?>.

+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/debitcard.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/debitcard.phtml new file mode 100644 index 00000000..929169b6 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/debitcard.phtml @@ -0,0 +1,13 @@ +getOrder(); +$payment = $this->getPayment(); +?> +
+

getBillingAddress()->getFirstname()) ?>, su pago de + formatPriceWithLocalCurrency($this->currencyCode, $payment->getEbanxLocalAmount()); ?> + fue aprobado o/

+

Si tiene alguna duda acerca de su pago, acceda a la cuenta EBANX con el correo electrónico + getBillingAddress()->getEmail() ?>.

+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/otroscupones.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/otroscupones.phtml new file mode 100644 index 00000000..2190aff5 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/otroscupones.phtml @@ -0,0 +1,31 @@ +getOrder(); +$payment = $this->getPayment(); +$dueDate = $this->helper->getDueDate($order->getEbanxDueDate(), 'dd/MM'); +?> +
+

¡Listo getBillingAddress()->getFirstname() ?>! Tu cupon EBANX de pago ha sido generada.

+ +

Enviamos una copia a getBillingAddress()->getEmail() ?>.

+ +

No lo olvides: tu boleta vence el día . Después de esa fecha no será posible + realizar el pago y la boleta será cancelada automáticamente.

+ +

Si tienes dudas, por favor escribe a soporte@ebanx.com.

+ +
+ + +
+ +
+ +
+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/oxxo.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/oxxo.phtml new file mode 100644 index 00000000..9bd53203 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/oxxo.phtml @@ -0,0 +1,31 @@ +getOrder(); +$payment = $this->getPayment(); +$dueDate = $this->helper->getDueDate($order->getEbanxDueDate(), 'dd/MM'); +?> +
+

¡Listo getBillingAddress()->getFirstname() ?>! Tu boleta EBANX de pago en OXXO ha sido generada.

+ +

Enviamos una copia a getBillingAddress()->getEmail() ?>.

+ +

No lo olvides: tu boleta vence el día . Después de esa fecha no será posible + realizar el pago y la boleta será cancelada automáticamente.

+ +

¿Dudas? Con gusto te ayudaremos.

+ +
+ + +
+ +
+ +
+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/pagoefectivo.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/pagoefectivo.phtml new file mode 100644 index 00000000..e2f65de6 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/pagoefectivo.phtml @@ -0,0 +1,22 @@ +getOrder(); +$payment = $this->getPayment(); +?> +
+

¡Listo getBillingAddress()->getFirstname() ?>!

+

Acabamos de confirmar la operación y procesaremos tu orden. Imprime tu cupón y acércate a cualquier centro + autorizado para realizar tu pago.

+

Una copia del cupón fue enviada al correo electrónico: getBillingAddress()->getEmail() ?>

+

Si tienes dudas, por favor escribe a soporte@ebanx.com.

+ +
+ Imprimir Voucher +
+ +
+ +
+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/pagofacil.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/pagofacil.phtml new file mode 100644 index 00000000..d46d58bc --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/pagofacil.phtml @@ -0,0 +1,31 @@ +getOrder(); +$payment = $this->getPayment(); +$dueDate = $this->helper->getDueDate($order->getEbanxDueDate(), 'dd/MM'); +?> +
+

¡Listo getBillingAddress()->getFirstname() ?>! Tu boleta EBANX de pago en Pago Facil ha sido generada.

+ +

Enviamos una copia a getBillingAddress()->getEmail() ?>.

+ +

No lo olvides: tu boleta vence el día . Después de esa fecha no será posible + realizar el pago y la boleta será cancelada automáticamente.

+ +

Si tienes dudas, por favor escribe a soporte@ebanx.com.

+ +
+ + +
+ +
+ +
+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/rapipago.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/rapipago.phtml new file mode 100644 index 00000000..3067f1a0 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/rapipago.phtml @@ -0,0 +1,31 @@ +getOrder(); +$payment = $this->getPayment(); +$dueDate = $this->helper->getDueDate($order->getEbanxDueDate(), 'dd/MM'); +?> +
+

¡Listo getBillingAddress()->getFirstname() ?>! Tu boleta EBANX de pago en Rapipago ha sido generada.

+ +

Enviamos una copia a getBillingAddress()->getEmail() ?>.

+ +

No lo olvides: tu boleta vence el día . Después de esa fecha no será posible + realizar el pago y la boleta será cancelada automáticamente.

+ +

Si tienes dudas, por favor escribe a soporte@ebanx.com.

+ +
+ + +
+ +
+ +
+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/checkout/success/spei.phtml b/app/design/frontend/base/default/template/ebanx/checkout/success/spei.phtml new file mode 100644 index 00000000..3776401e --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/checkout/success/spei.phtml @@ -0,0 +1,31 @@ +getOrder(); +$payment = $this->getPayment(); +$dueDate = $this->helper->getDueDate($order->getEbanxDueDate(), 'dd/MM'); +?> +
+

¡Listo getBillingAddress()->getFirstname() ?>! Tu boleta EBANX de pago en SPEI ha sido generada.

+ +

Enviamos una copia a getBillingAddress()->getEmail() ?>.

+ +

No lo olvides: tu boleta vence el día . Después de esa fecha no será posible + realizar el pago.

+ +

Si tienes dudas, por favor escribe a soporte@ebanx.com.

+ +
+ + +
+ +
+ +
+ + +
diff --git a/app/design/frontend/base/default/template/ebanx/customer/account/create.phtml b/app/design/frontend/base/default/template/ebanx/customer/account/create.phtml new file mode 100644 index 00000000..1fab3315 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/customer/account/create.phtml @@ -0,0 +1,24 @@ + + +isLoggedIn() && $session->getCustomer()->getDefaultBillingAddress()): + $countryCode = $session->getCustomer()->getDefaultBillingAddress()->getCountry(); + + if ($countryCode) { + $label = Mage::helper('ebanx')->getLabelForComplianceFieldByCountry($countryCode); + } + ?> + + +
+

__('Payment cards') ?>

+
+ +getMessagesBlock()->getGroupedHtml() ?> +getChildHtml('pager'); ?> + +getCards()->getSize()): ?> +
+ + + + + + + + + + + + + getCards() as $card): ?> + + + + + + +
__('Card') ?>
+ + + +
+ + + + getChildHtml('pager') ?> + +
+ + +
+
+ + +

__('You have no saved credit cards yet.') ?>

+
+ +
+ + diff --git a/app/design/frontend/base/default/template/ebanx/form/baloto.phtml b/app/design/frontend/base/default/template/ebanx/form/baloto.phtml new file mode 100644 index 00000000..28a5c6b4 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/baloto.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
' . $warningTexts['tag'] . '
' : ''; + +echo $showInlineIcon ? '
' . $icon . $sandboxTag . '
' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/boleto.phtml b/app/design/frontend/base/default/template/ebanx/form/boleto.phtml new file mode 100644 index 00000000..d8506b3f --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/boleto.phtml @@ -0,0 +1,26 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
' . $warningTexts['tag'] . '
' : ''; + +echo $showInlineIcon ? '
' . $icon . $sandboxTag . '
' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/brazil/local_amount.phtml b/app/design/frontend/base/default/template/ebanx/form/brazil/local_amount.phtml new file mode 100644 index 00000000..f01e1426 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/brazil/local_amount.phtml @@ -0,0 +1,5 @@ + +
+

getLocalAmount('BRL') : $this->getLocalAmountWithoutTax('BRL') ?>

+ +
diff --git a/app/design/frontend/base/default/template/ebanx/form/card/card-form.phtml b/app/design/frontend/base/default/template/ebanx/form/card/card-form.phtml new file mode 100644 index 00000000..be86299e --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/card/card-form.phtml @@ -0,0 +1,81 @@ +
+ + + + + diff --git a/app/design/frontend/base/default/template/ebanx/form/card/instalments.phtml b/app/design/frontend/base/default/template/ebanx/form/card/instalments.phtml new file mode 100644 index 00000000..2147f982 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/card/instalments.phtml @@ -0,0 +1,29 @@ +getInstalmentTerms(); +$_selected = $this->getInfoData('instalments'); +?> + 1): ?> +
  • + +
    + + + + +
    +
  • + diff --git a/app/design/frontend/base/default/template/ebanx/form/card/main.phtml b/app/design/frontend/base/default/template/ebanx/form/card/main.phtml new file mode 100644 index 00000000..fa7ae380 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/card/main.phtml @@ -0,0 +1,77 @@ +getText(); + $lowerCountry = $this->country; + $localCurrency = $this->localCurrency; + $skin = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_SKIN); + $_code = $this->getMethodCode(); + $showInlineIcon = Mage::helper('ebanx')->hasToShowInlineIcon(); + $icon = ''; + $showIof = Mage::getStoreConfig('payment/ebanx_settings/iof_local_amount'); + $warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); + $isSandbox = Mage::helper('ebanx')->isSandboxMode(); + $sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + + + + diff --git a/app/design/frontend/base/default/template/ebanx/form/card/saved-card.phtml b/app/design/frontend/base/default/template/ebanx/form/card/saved-card.phtml new file mode 100644 index 00000000..a93f4224 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/card/saved-card.phtml @@ -0,0 +1,32 @@ + +
  • + + + +
    +
    + +
    +
    + +
    +
    +
    + + + + + + +
  • diff --git a/app/design/frontend/base/default/template/ebanx/form/creditcard_ar.phtml b/app/design/frontend/base/default/template/ebanx/form/creditcard_ar.phtml new file mode 100644 index 00000000..3cb11d1d --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/creditcard_ar.phtml @@ -0,0 +1,2 @@ +getMethodCode(); +$skin = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_SKIN); +$showInlineIcon = Mage::helper('ebanx')->hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + + + diff --git a/app/design/frontend/base/default/template/ebanx/form/fields.phtml b/app/design/frontend/base/default/template/ebanx/form/fields.phtml new file mode 100644 index 00000000..fc77039d --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/fields.phtml @@ -0,0 +1,60 @@ +getQuote()->getShippingAddress()->getCountryId(); + +if (Mage::getSingleton('customer/session')->isLoggedIn()) { + $customer_id = Mage::getSingleton('customer/session')->getCustomer()->getId(); + $customer = Mage::getModel('customer/customer')->load($customer_id); + $ebanxCustomerDocument = $customer->getEbanxCustomerDocument(); + $ebanxCustomerDocumentType = $customer->getEbanxCustomerDocumentType(); +} + +if ($helper->hasDocumentFieldAlreadyForMethod($this->getMethodCode()) && (!isset($customer) || $country !== 'AR' || $ebanxCustomerDocumentType)) { + return; +} + +?> +
  • + + +
    + +
    + + +
    + +
    + + +
  • diff --git a/app/design/frontend/base/default/template/ebanx/form/multicaja.phtml b/app/design/frontend/base/default/template/ebanx/form/multicaja.phtml new file mode 100644 index 00000000..a2452aed --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/multicaja.phtml @@ -0,0 +1,27 @@ +hasToShowInlineIcon(); +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/otroscupones.phtml b/app/design/frontend/base/default/template/ebanx/form/otroscupones.phtml new file mode 100644 index 00000000..ec03432d --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/otroscupones.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/oxxo.phtml b/app/design/frontend/base/default/template/ebanx/form/oxxo.phtml new file mode 100644 index 00000000..d7d482fa --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/oxxo.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/pagoefectivo.phtml b/app/design/frontend/base/default/template/ebanx/form/pagoefectivo.phtml new file mode 100644 index 00000000..a8276c70 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/pagoefectivo.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/pagofacil.phtml b/app/design/frontend/base/default/template/ebanx/form/pagofacil.phtml new file mode 100644 index 00000000..fd4fbd3c --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/pagofacil.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/pse.phtml b/app/design/frontend/base/default/template/ebanx/form/pse.phtml new file mode 100644 index 00000000..0fa79afa --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/pse.phtml @@ -0,0 +1,37 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/rapipago.phtml b/app/design/frontend/base/default/template/ebanx/form/rapipago.phtml new file mode 100644 index 00000000..3f6b2cf1 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/rapipago.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/safetypayec.phtml b/app/design/frontend/base/default/template/ebanx/form/safetypayec.phtml new file mode 100644 index 00000000..1092316a --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/safetypayec.phtml @@ -0,0 +1,40 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/safetypaype.phtml b/app/design/frontend/base/default/template/ebanx/form/safetypaype.phtml new file mode 100644 index 00000000..eb086cb2 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/safetypaype.phtml @@ -0,0 +1,40 @@ +hasToShowInlineIcon(); +$icon = ''; +$$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/sandbox-warning.phtml b/app/design/frontend/base/default/template/ebanx/form/sandbox-warning.phtml new file mode 100644 index 00000000..d361ac76 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/sandbox-warning.phtml @@ -0,0 +1,14 @@ +isSandboxMode()) { + return; +} + +$skinsPath = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_SKIN); + +?> + +
  • + +

    +
  • diff --git a/app/design/frontend/base/default/template/ebanx/form/sencillito.phtml b/app/design/frontend/base/default/template/ebanx/form/sencillito.phtml new file mode 100644 index 00000000..80d39de0 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/sencillito.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/servipag.phtml b/app/design/frontend/base/default/template/ebanx/form/servipag.phtml new file mode 100644 index 00000000..03f50d5e --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/servipag.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/spei.phtml b/app/design/frontend/base/default/template/ebanx/form/spei.phtml new file mode 100644 index 00000000..11141d4f --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/spei.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/tef.phtml b/app/design/frontend/base/default/template/ebanx/form/tef.phtml new file mode 100644 index 00000000..021e9c95 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/tef.phtml @@ -0,0 +1,52 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/wallet.phtml b/app/design/frontend/base/default/template/ebanx/form/wallet.phtml new file mode 100644 index 00000000..addc912e --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/wallet.phtml @@ -0,0 +1,26 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/form/webpay.phtml b/app/design/frontend/base/default/template/ebanx/form/webpay.phtml new file mode 100644 index 00000000..65b79bdb --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/form/webpay.phtml @@ -0,0 +1,28 @@ +hasToShowInlineIcon(); +$icon = ''; +$warningTexts = Mage::helper('ebanx')->getSandboxWarningText(); +$isSandbox = Mage::helper('ebanx')->isSandboxMode(); +$sandboxTag = $isSandbox ? '
    ' . $warningTexts['tag'] . '
    ' : ''; + +echo $showInlineIcon ? '
    ' . $icon . $sandboxTag . '
    ' : null; +?> + + diff --git a/app/design/frontend/base/default/template/ebanx/info/baloto.phtml b/app/design/frontend/base/default/template/ebanx/info/baloto.phtml new file mode 100644 index 00000000..1e782598 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/baloto.phtml @@ -0,0 +1,5 @@ +

    Baloto

    +
    +

    Total a pagar en Peso colombiano: getLocalAmountWithoutTax('COP') ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/boleto.phtml b/app/design/frontend/base/default/template/ebanx/info/boleto.phtml new file mode 100644 index 00000000..46714ea6 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/boleto.phtml @@ -0,0 +1,13 @@ +

    Boleto Bancário

    +
    +

    shouldntShowIof() ? 'Total a pagar: ' : 'Total a pagar com IOF (0.38%): '; + + if (strpos(Mage::helper('core/url')->getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('BRL'); + } else { + echo $this->getLocalAmountWithoutTax('BRL'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/creditcard_ar.phtml b/app/design/frontend/base/default/template/ebanx/info/creditcard_ar.phtml new file mode 100644 index 00000000..98dcdbe7 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/creditcard_ar.phtml @@ -0,0 +1,5 @@ +

    Tarjeta de Credito

    +
    +

    Total a pagar en Peso Argentino : getLocalAmountWithoutTax('ARS') ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/creditcard_br.phtml b/app/design/frontend/base/default/template/ebanx/info/creditcard_br.phtml new file mode 100644 index 00000000..d6a5ce2d --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/creditcard_br.phtml @@ -0,0 +1,13 @@ +

    Cartão de Crédito

    +
    +

    shouldntShowIof() ? 'Total a pagar: ' : 'Total a pagar com IOF (0.38%): '; + + if (strpos(Mage::helper('core/url')->getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('BRL'); + } else { + echo $this->getLocalAmountWithoutTax('BRL'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/creditcard_co.phtml b/app/design/frontend/base/default/template/ebanx/info/creditcard_co.phtml new file mode 100644 index 00000000..73eb216f --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/creditcard_co.phtml @@ -0,0 +1,5 @@ +

    Tarjeta de Credito

    +
    +

    Total a pagar en Peso Colombiano : getLocalAmountWithoutTax('COP') ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/creditcard_mx.phtml b/app/design/frontend/base/default/template/ebanx/info/creditcard_mx.phtml new file mode 100644 index 00000000..9c8abb86 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/creditcard_mx.phtml @@ -0,0 +1,5 @@ +

    Tarjeta de Credito

    +
    +

    Total a pagar en Peso mexicano: getLocalAmountWithoutTax('MXN') ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/debitcard.phtml b/app/design/frontend/base/default/template/ebanx/info/debitcard.phtml new file mode 100644 index 00000000..f0498125 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/debitcard.phtml @@ -0,0 +1,5 @@ +

    Tarjeta de Debito

    +
    +

    Total a pagar en Peso mexicano: getLocalAmountWithoutTax('MXN') ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/multicaja.phtml b/app/design/frontend/base/default/template/ebanx/info/multicaja.phtml new file mode 100644 index 00000000..b0a307b6 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/multicaja.phtml @@ -0,0 +1,5 @@ +

    Multicaja

    +
    +

    Total a pagar en Peso chileno: getLocalAmount('CLP') ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/otroscupones.phtml b/app/design/frontend/base/default/template/ebanx/info/otroscupones.phtml new file mode 100644 index 00000000..8f38d788 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/otroscupones.phtml @@ -0,0 +1,13 @@ +

    Otros Cupones

    +
    +

    Total a pagar en Peso argentino: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('ARS'); + } else { + echo $this->getLocalAmountWithoutTax('ARS'); + } + ?>

    + +getChildHtml() ?> + diff --git a/app/design/frontend/base/default/template/ebanx/info/oxxo.phtml b/app/design/frontend/base/default/template/ebanx/info/oxxo.phtml new file mode 100644 index 00000000..9d89d26c --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/oxxo.phtml @@ -0,0 +1,13 @@ +

    OXXO

    +
    +

    Total a pagar en Peso mexicano: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('MXN'); + } else { + echo $this->getLocalAmountWithoutTax('MXN'); + } + ?>

    + +getChildHtml() ?> + diff --git a/app/design/frontend/base/default/template/ebanx/info/pagoefectivo.phtml b/app/design/frontend/base/default/template/ebanx/info/pagoefectivo.phtml new file mode 100644 index 00000000..ee8c3007 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/pagoefectivo.phtml @@ -0,0 +1,12 @@ +

    PagoEfectivo

    +
    +

    Total a pagar en Sol peruano: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('PEN'); + } else { + echo $this->getLocalAmountWithoutTax('PEN'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/pagofacil.phtml b/app/design/frontend/base/default/template/ebanx/info/pagofacil.phtml new file mode 100644 index 00000000..ca7616fc --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/pagofacil.phtml @@ -0,0 +1,13 @@ +

    Pago Facil

    +
    +

    Total a pagar en Peso argentino: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('ARS'); + } else { + echo $this->getLocalAmountWithoutTax('ARS'); + } + ?>

    + +getChildHtml() ?> + diff --git a/app/design/frontend/base/default/template/ebanx/info/pse.phtml b/app/design/frontend/base/default/template/ebanx/info/pse.phtml new file mode 100644 index 00000000..fc204e82 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/pse.phtml @@ -0,0 +1,12 @@ +

    PSE - Pago Seguros en Línea

    +
    +

    Total a pagar en Peso colombiano: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('COP'); + } else { + echo $this->getLocalAmountWithoutTax('COP'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/rapipago.phtml b/app/design/frontend/base/default/template/ebanx/info/rapipago.phtml new file mode 100644 index 00000000..c8f572ff --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/rapipago.phtml @@ -0,0 +1,13 @@ +

    Rapipago

    +
    +

    Total a pagar en Peso argentino: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('ARS'); + } else { + echo $this->getLocalAmountWithoutTax('ARS'); + } + ?>

    + +getChildHtml() ?> + diff --git a/app/design/frontend/base/default/template/ebanx/info/safetypayec.phtml b/app/design/frontend/base/default/template/ebanx/info/safetypayec.phtml new file mode 100644 index 00000000..1d060372 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/safetypayec.phtml @@ -0,0 +1,12 @@ +

    SafetyPay

    +
    +

    Total a pagar en dolar: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('USD'); + } else { + echo $this->getLocalAmountWithoutTax('USD'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/safetypaype.phtml b/app/design/frontend/base/default/template/ebanx/info/safetypaype.phtml new file mode 100644 index 00000000..2db76ca3 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/safetypaype.phtml @@ -0,0 +1,12 @@ +

    SafetyPay

    +
    +

    Total a pagar en Sol peruano: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('PEN'); + } else { + echo $this->getLocalAmountWithoutTax('PEN'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/sencillito.phtml b/app/design/frontend/base/default/template/ebanx/info/sencillito.phtml new file mode 100644 index 00000000..02d24ab6 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/sencillito.phtml @@ -0,0 +1,12 @@ +

    Sencillito

    +
    +

    Total a pagar en Peso chileno: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('CLP'); + } else { + echo $this->getLocalAmountWithoutTax('CLP'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/servipag.phtml b/app/design/frontend/base/default/template/ebanx/info/servipag.phtml new file mode 100644 index 00000000..796b3f5e --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/servipag.phtml @@ -0,0 +1,12 @@ +

    Servipag

    +
    +

    Total a pagar en Peso chileno: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('CLP'); + } else { + echo $this->getLocalAmountWithoutTax('CLP'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/spei.phtml b/app/design/frontend/base/default/template/ebanx/info/spei.phtml new file mode 100644 index 00000000..64696efb --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/spei.phtml @@ -0,0 +1,13 @@ +

    SPEI

    +
    +

    Total a pagar en Peso mexicano: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('MXN'); + } else { + echo $this->getLocalAmountWithoutTax('MXN'); + } + ?>

    + +getChildHtml() ?> + diff --git a/app/design/frontend/base/default/template/ebanx/info/tef.phtml b/app/design/frontend/base/default/template/ebanx/info/tef.phtml new file mode 100644 index 00000000..e1b431bc --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/tef.phtml @@ -0,0 +1,13 @@ +

    Débito Online

    +
    +

    shouldntShowIof() ? 'Total a pagar: ' : 'Total a pagar com IOF (0.38%): '; + + if (strpos(Mage::helper('core/url')->getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('BRL'); + } else { + echo $this->getLocalAmountWithoutTax('BRL'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/wallet.phtml b/app/design/frontend/base/default/template/ebanx/info/wallet.phtml new file mode 100644 index 00000000..a01e1ec9 --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/wallet.phtml @@ -0,0 +1,13 @@ +

    Saldo EBANX

    +
    +

    shouldntShowIof() ? 'Total a pagar: ' : 'Total a pagar com IOF (0.38%): '; + + if (strpos(Mage::helper('core/url')->getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('BRL'); + } else { + echo $this->getLocalAmountWithoutTax('BRL'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/design/frontend/base/default/template/ebanx/info/webpay.phtml b/app/design/frontend/base/default/template/ebanx/info/webpay.phtml new file mode 100644 index 00000000..e1a0ca8c --- /dev/null +++ b/app/design/frontend/base/default/template/ebanx/info/webpay.phtml @@ -0,0 +1,12 @@ +

    Webpay

    +
    +

    Total a pagar en Peso chileno: getCurrentUrl(), Mage::helper('checkout/url')->getCheckoutUrl()) !== false) { + echo $this->getLocalAmount('CLP'); + } else { + echo $this->getLocalAmountWithoutTax('CLP'); + } + ?>

    + +getChildHtml() ?> diff --git a/app/etc/modules/Ebanx_Gateway.xml b/app/etc/modules/Ebanx_Gateway.xml new file mode 100644 index 00000000..92eeb0c6 --- /dev/null +++ b/app/etc/modules/Ebanx_Gateway.xml @@ -0,0 +1,12 @@ + + + + + true + community + + + + + + diff --git a/app/locale/de_DE/Mage_Cms.csv b/app/locale/de_DE/Mage_Cms.csv index e2d7b7ef..393b9017 100644 --- a/app/locale/de_DE/Mage_Cms.csv +++ b/app/locale/de_DE/Mage_Cms.csv @@ -112,11 +112,7 @@ "The page URL key contains capital letters or disallowed symbols.","Die URL dieser Seite enthält Großbuchstaben oder unerlaubte Symbole." "The page has been deleted.","Die Seite wurde gelöscht." "The page has been saved.","Die Seite wurde gespeichert." -"The static block content cannot contain directive with its self.","Der statische Block selbst darf keine Anweisungen enthalten. - - - -" +"The static block content cannot contain directive with its self.","Der statische Block selbst darf keine Anweisungen enthalten." "This block no longer exists.","Dieser Block existiert nicht mehr." "This page no longer exists.","Diese Seite existiert nicht mehr." "Title","Titel" @@ -125,3 +121,4 @@ "Unable to find a page to delete.","Es kann keine Seite zum Löschen gefunden werden." "WYSIWYG Options","WYSIWYG Optionen" "px.","px." +"NEW","Neu" diff --git a/app/locale/es_ES/Mage_Cms.csv b/app/locale/es_ES/Mage_Cms.csv index 69eeff80..1e25f636 100644 --- a/app/locale/es_ES/Mage_Cms.csv +++ b/app/locale/es_ES/Mage_Cms.csv @@ -121,3 +121,4 @@ "Unable to find a page to delete.","No se puede encontrar una página para eliminar." "WYSIWYG Options","Opciones de formato WYSIWYG" "px.","px." +"NEW","NOVEDADES" diff --git a/app/locale/fr_FR/Mage_Cms.csv b/app/locale/fr_FR/Mage_Cms.csv index b3565fbd..3926af15 100644 --- a/app/locale/fr_FR/Mage_Cms.csv +++ b/app/locale/fr_FR/Mage_Cms.csv @@ -126,4 +126,5 @@ "Firebug is known to make the WYSIWYG editor slow unless it is turned off or configured properly.","Firebug peut ralentir l'éditeur WYSIWYG à moins de le désactiver ou de le configurer correctement." "Hide","Masquer" "Insert Media...","Insérer un media..." -"Insert File...","Insérer un fichier..." \ No newline at end of file +"Insert File...","Insérer un fichier..." +"NEW","NOUVEAUTÉS" \ No newline at end of file diff --git a/app/locale/it_IT/Mage_Cms.csv b/app/locale/it_IT/Mage_Cms.csv index 6f7a273b..64bd06e9 100644 --- a/app/locale/it_IT/Mage_Cms.csv +++ b/app/locale/it_IT/Mage_Cms.csv @@ -128,3 +128,4 @@ "Warning","Attenzione" "Firebug is known to make the WYSIWYG editor slow unless it is turned off or configured properly.","Firebug potrebbe rendere l'editor WYSIWYG più lento a meno che non sia disattivato o configurato correttamente" "Hide","Nascondi" +"NEW","Nuovo" \ No newline at end of file diff --git a/app/locale/nl_NL/Mage_Cms.csv b/app/locale/nl_NL/Mage_Cms.csv index 7d793fad..91a6ae2a 100644 --- a/app/locale/nl_NL/Mage_Cms.csv +++ b/app/locale/nl_NL/Mage_Cms.csv @@ -121,3 +121,4 @@ "Unable to find a page to delete.","De te verwijderen pagina is niet gevonden" "WYSIWYG Options","WYSIWYG Opties" "px.","pixels" +"NEW","Nieuw" \ No newline at end of file diff --git a/app/locale/pt_PT/Mage_Cms.csv b/app/locale/pt_PT/Mage_Cms.csv index e3ed87ff..ada435c1 100644 --- a/app/locale/pt_PT/Mage_Cms.csv +++ b/app/locale/pt_PT/Mage_Cms.csv @@ -138,3 +138,4 @@ "Unable to find a page to delete.","Não foi possível encontrar a página a remover" "URL Key","Chave URL" "WYSIWYG Options","Opções WYSIWYG" +"NEW","Novidade" diff --git a/js/ebanx/chosen/LICENSE.md b/js/ebanx/chosen/LICENSE.md new file mode 100644 index 00000000..ff3ef3b1 --- /dev/null +++ b/js/ebanx/chosen/LICENSE.md @@ -0,0 +1,23 @@ +#### Chosen +- by Patrick Filler for [Harvest](http://getharvest.com) +- Copyright (c) 2011-2014 by Harvest + +Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/js/ebanx/chosen/chosen.proto.js b/js/ebanx/chosen/chosen.proto.js new file mode 100644 index 00000000..2d190a45 --- /dev/null +++ b/js/ebanx/chosen/chosen.proto.js @@ -0,0 +1,1190 @@ +// Chosen, a Select Box Enhancer for jQuery and Prototype +// by Patrick Filler for Harvest, http://getharvest.com +// +// Version 1.0.0 +// Full source at https://github.com/harvesthq/chosen +// Copyright (c) 2011 Harvest http://getharvest.com + +// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md +// This file is generated by `grunt build`, do not edit it by hand. +; +(function() { + var AbstractChosen, SelectParser, _ref, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + SelectParser = (function() { + function SelectParser() { + this.options_index = 0; + this.parsed = []; + } + + SelectParser.prototype.add_node = function(child) { + if (child.nodeName.toUpperCase() === "OPTGROUP") { + return this.add_group(child); + } else { + return this.add_option(child); + } + }; + + SelectParser.prototype.add_group = function(group) { + var group_position, option, _i, _len, _ref, _results; + + group_position = this.parsed.length; + this.parsed.push({ + array_index: group_position, + group: true, + label: this.escapeExpression(group.label), + children: 0, + disabled: group.disabled + }); + _ref = group.childNodes; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + _results.push(this.add_option(option, group_position, group.disabled)); + } + return _results; + }; + + SelectParser.prototype.add_option = function(option, group_position, group_disabled) { + if (option.nodeName.toUpperCase() === "OPTION") { + if (option.text !== "") { + if (group_position != null) { + this.parsed[group_position].children += 1; + } + this.parsed.push({ + array_index: this.parsed.length, + options_index: this.options_index, + value: option.value, + text: option.text, + html: option.innerHTML, + selected: option.selected, + disabled: group_disabled === true ? group_disabled : option.disabled, + group_array_index: group_position, + classes: option.className, + style: option.style.cssText + }); + } else { + this.parsed.push({ + array_index: this.parsed.length, + options_index: this.options_index, + empty: true + }); + } + return this.options_index += 1; + } + }; + + SelectParser.prototype.escapeExpression = function(text) { + var map, unsafe_chars; + + if ((text == null) || text === false) { + return ""; + } + if (!/[\&\<\>\"\'\`]/.test(text)) { + return text; + } + map = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + unsafe_chars = /&(?!\w+;)|[\<\>\"\'\`]/g; + return text.replace(unsafe_chars, function(chr) { + return map[chr] || "&"; + }); + }; + + return SelectParser; + + })(); + + SelectParser.select_to_array = function(select) { + var child, parser, _i, _len, _ref; + + parser = new SelectParser(); + _ref = select.childNodes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + parser.add_node(child); + } + return parser.parsed; + }; + + AbstractChosen = (function() { + function AbstractChosen(form_field, options) { + this.form_field = form_field; + this.options = options != null ? options : {}; + if (!AbstractChosen.browser_is_supported()) { + return; + } + this.is_multiple = this.form_field.multiple; + this.set_default_text(); + this.set_default_values(); + this.setup(); + this.set_up_html(); + this.register_observers(); + } + + AbstractChosen.prototype.set_default_values = function() { + var _this = this; + + this.click_test_action = function(evt) { + return _this.test_active_click(evt); + }; + this.activate_action = function(evt) { + return _this.activate_field(evt); + }; + this.active_field = false; + this.mouse_on_container = false; + this.results_showing = false; + this.result_highlighted = null; + this.result_single_selected = null; + this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false; + this.disable_search_threshold = this.options.disable_search_threshold || 0; + this.disable_search = this.options.disable_search || false; + this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true; + this.group_search = this.options.group_search != null ? this.options.group_search : true; + this.search_contains = this.options.search_contains || false; + this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true; + this.max_selected_options = this.options.max_selected_options || Infinity; + this.inherit_select_classes = this.options.inherit_select_classes || false; + this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true; + return this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true; + }; + + AbstractChosen.prototype.set_default_text = function() { + if (this.form_field.getAttribute("data-placeholder")) { + this.default_text = this.form_field.getAttribute("data-placeholder"); + } else if (this.is_multiple) { + this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text; + } else { + this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text; + } + return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text; + }; + + AbstractChosen.prototype.mouse_enter = function() { + return this.mouse_on_container = true; + }; + + AbstractChosen.prototype.mouse_leave = function() { + return this.mouse_on_container = false; + }; + + AbstractChosen.prototype.input_focus = function(evt) { + var _this = this; + + if (this.is_multiple) { + if (!this.active_field) { + return setTimeout((function() { + return _this.container_mousedown(); + }), 50); + } + } else { + if (!this.active_field) { + return this.activate_field(); + } + } + }; + + AbstractChosen.prototype.input_blur = function(evt) { + var _this = this; + + if (!this.mouse_on_container) { + this.active_field = false; + return setTimeout((function() { + return _this.blur_test(); + }), 100); + } + }; + + AbstractChosen.prototype.results_option_build = function(options) { + var content, data, _i, _len, _ref; + + content = ''; + _ref = this.results_data; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + data = _ref[_i]; + if (data.group) { + content += this.result_add_group(data); + } else { + content += this.result_add_option(data); + } + if (options != null ? options.first : void 0) { + if (data.selected && this.is_multiple) { + this.choice_build(data); + } else if (data.selected && !this.is_multiple) { + this.single_set_selected_text(data.text); + } + } + } + return content; + }; + + AbstractChosen.prototype.result_add_option = function(option) { + var classes, style; + + if (!option.search_match) { + return ''; + } + if (!this.include_option_in_results(option)) { + return ''; + } + classes = []; + if (!option.disabled && !(option.selected && this.is_multiple)) { + classes.push("active-result"); + } + if (option.disabled && !(option.selected && this.is_multiple)) { + classes.push("disabled-result"); + } + if (option.selected) { + classes.push("result-selected"); + } + if (option.group_array_index != null) { + classes.push("group-option"); + } + if (option.classes !== "") { + classes.push(option.classes); + } + style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : ""; + return "
  • " + option.search_text + "
  • "; + }; + + AbstractChosen.prototype.result_add_group = function(group) { + if (!(group.search_match || group.group_match)) { + return ''; + } + if (!(group.active_options > 0)) { + return ''; + } + return "
  • " + group.search_text + "
  • "; + }; + + AbstractChosen.prototype.results_update_field = function() { + this.set_default_text(); + if (!this.is_multiple) { + this.results_reset_cleanup(); + } + this.result_clear_highlight(); + this.result_single_selected = null; + this.results_build(); + if (this.results_showing) { + return this.winnow_results(); + } + }; + + AbstractChosen.prototype.results_toggle = function() { + if (this.results_showing) { + return this.results_hide(); + } else { + return this.results_show(); + } + }; + + AbstractChosen.prototype.results_search = function(evt) { + if (this.results_showing) { + return this.winnow_results(); + } else { + return this.results_show(); + } + }; + + AbstractChosen.prototype.winnow_results = function() { + var escapedSearchText, option, regex, regexAnchor, results, results_group, searchText, startpos, text, zregex, _i, _len, _ref; + + this.no_results_clear(); + results = 0; + searchText = this.get_search_text(); + escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + regexAnchor = this.search_contains ? "" : "^"; + regex = new RegExp(regexAnchor + escapedSearchText, 'i'); + zregex = new RegExp(escapedSearchText, 'i'); + _ref = this.results_data; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + option.search_match = false; + results_group = null; + if (this.include_option_in_results(option)) { + if (option.group) { + option.group_match = false; + option.active_options = 0; + } + if ((option.group_array_index != null) && this.results_data[option.group_array_index]) { + results_group = this.results_data[option.group_array_index]; + if (results_group.active_options === 0 && results_group.search_match) { + results += 1; + } + results_group.active_options += 1; + } + if (!(option.group && !this.group_search)) { + option.search_text = option.group ? option.label : option.html; + option.search_match = this.search_string_match(option.search_text, regex); + if (option.search_match && !option.group) { + results += 1; + } + if (option.search_match) { + if (searchText.length) { + startpos = option.search_text.search(zregex); + text = option.search_text.substr(0, startpos + searchText.length) + '' + option.search_text.substr(startpos + searchText.length); + option.search_text = text.substr(0, startpos) + '' + text.substr(startpos); + } + if (results_group != null) { + results_group.group_match = true; + } + } else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) { + option.search_match = true; + } + } + } + } + this.result_clear_highlight(); + if (results < 1 && searchText.length) { + this.update_results_content(""); + return this.no_results(searchText); + } else { + this.update_results_content(this.results_option_build()); + return this.winnow_results_set_highlight(); + } + }; + + AbstractChosen.prototype.search_string_match = function(search_string, regex) { + var part, parts, _i, _len; + + if (regex.test(search_string)) { + return true; + } else if (this.enable_split_word_search && (search_string.indexOf(" ") >= 0 || search_string.indexOf("[") === 0)) { + parts = search_string.replace(/\[|\]/g, "").split(" "); + if (parts.length) { + for (_i = 0, _len = parts.length; _i < _len; _i++) { + part = parts[_i]; + if (regex.test(part)) { + return true; + } + } + } + } + }; + + AbstractChosen.prototype.choices_count = function() { + var option, _i, _len, _ref; + + if (this.selected_option_count != null) { + return this.selected_option_count; + } + this.selected_option_count = 0; + _ref = this.form_field.options; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + if (option.selected) { + this.selected_option_count += 1; + } + } + return this.selected_option_count; + }; + + AbstractChosen.prototype.choices_click = function(evt) { + evt.preventDefault(); + if (!(this.results_showing || this.is_disabled)) { + return this.results_show(); + } + }; + + AbstractChosen.prototype.keyup_checker = function(evt) { + var stroke, _ref; + + stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; + this.search_field_scale(); + switch (stroke) { + case 8: + if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) { + return this.keydown_backstroke(); + } else if (!this.pending_backstroke) { + this.result_clear_highlight(); + return this.results_search(); + } + break; + case 13: + evt.preventDefault(); + if (this.results_showing) { + return this.result_select(evt); + } + break; + case 27: + if (this.results_showing) { + this.results_hide(); + } + return true; + case 9: + case 38: + case 40: + case 16: + case 91: + case 17: + break; + default: + return this.results_search(); + } + }; + + AbstractChosen.prototype.container_width = function() { + if (this.options.width != null) { + return this.options.width; + } else { + return "" + this.form_field.offsetWidth + "px"; + } + }; + + AbstractChosen.prototype.include_option_in_results = function(option) { + if (this.is_multiple && (!this.display_selected_options && option.selected)) { + return false; + } + if (!this.display_disabled_options && option.disabled) { + return false; + } + if (option.empty) { + return false; + } + return true; + }; + + AbstractChosen.browser_is_supported = function() { + if (window.navigator.appName === "Microsoft Internet Explorer") { + return document.documentMode >= 8; + } + // if (/iP(od|hone)/i.test(window.navigator.userAgent)) { + // return false; + // } + // if (/Android/i.test(window.navigator.userAgent)) { + // if (/Mobile/i.test(window.navigator.userAgent)) { + // return false; + // } + // } + return true; + }; + + AbstractChosen.default_multiple_text = "Select Some Options"; + + AbstractChosen.default_single_text = "Select an Option"; + + AbstractChosen.default_no_result_text = "No results match"; + + return AbstractChosen; + + })(); + + this.Chosen = (function(_super) { + __extends(Chosen, _super); + + function Chosen() { + _ref = Chosen.__super__.constructor.apply(this, arguments); + return _ref; + } + + Chosen.prototype.setup = function() { + this.current_selectedIndex = this.form_field.selectedIndex; + return this.is_rtl = this.form_field.hasClassName("chosen-rtl"); + }; + + Chosen.prototype.set_default_values = function() { + Chosen.__super__.set_default_values.call(this); + this.single_temp = new Template('#{default}
    '); + this.multi_temp = new Template('
    '); + return this.no_results_temp = new Template('
  • ' + this.results_none_found + ' "#{terms}"
  • '); + }; + + Chosen.prototype.set_up_html = function() { + var container_classes, container_props; + + container_classes = ["chosen-container"]; + container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single")); + if (this.inherit_select_classes && this.form_field.className) { + container_classes.push(this.form_field.className); + } + if (this.is_rtl) { + container_classes.push("chosen-rtl"); + } + container_props = { + 'class': container_classes.join(' '), + 'style': "width: " + (this.container_width()) + ";", + 'title': this.form_field.title + }; + if (this.form_field.id.length) { + container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen"; + } + this.container = this.is_multiple ? new Element('div', container_props).update(this.multi_temp.evaluate({ + "default": this.default_text + })) : new Element('div', container_props).update(this.single_temp.evaluate({ + "default": this.default_text + })); + this.form_field.hide().insert({ + after: this.container + }); + this.dropdown = this.container.down('div.chosen-drop'); + this.search_field = this.container.down('input'); + this.search_results = this.container.down('ul.chosen-results'); + this.search_field_scale(); + this.search_no_results = this.container.down('li.no-results'); + if (this.is_multiple) { + this.search_choices = this.container.down('ul.chosen-choices'); + this.search_container = this.container.down('li.search-field'); + } else { + this.search_container = this.container.down('div.chosen-search'); + this.selected_item = this.container.down('.chosen-single'); + } + this.results_build(); + this.set_tab_index(); + this.set_label_behavior(); + return this.form_field.fire("chosen:ready", { + chosen: this + }); + }; + + Chosen.prototype.register_observers = function() { + var _this = this; + + this.container.observe("mousedown", function(evt) { + return _this.container_mousedown(evt); + }); + this.container.observe("mouseup", function(evt) { + return _this.container_mouseup(evt); + }); + this.container.observe("mouseenter", function(evt) { + return _this.mouse_enter(evt); + }); + this.container.observe("mouseleave", function(evt) { + return _this.mouse_leave(evt); + }); + this.search_results.observe("mouseup", function(evt) { + return _this.search_results_mouseup(evt); + }); + this.search_results.observe("mouseover", function(evt) { + return _this.search_results_mouseover(evt); + }); + this.search_results.observe("mouseout", function(evt) { + return _this.search_results_mouseout(evt); + }); + this.search_results.observe("mousewheel", function(evt) { + return _this.search_results_mousewheel(evt); + }); + this.search_results.observe("DOMMouseScroll", function(evt) { + return _this.search_results_mousewheel(evt); + }); + this.form_field.observe("chosen:updated", function(evt) { + return _this.results_update_field(evt); + }); + this.form_field.observe("chosen:activate", function(evt) { + return _this.activate_field(evt); + }); + this.form_field.observe("chosen:open", function(evt) { + return _this.container_mousedown(evt); + }); + this.search_field.observe("blur", function(evt) { + return _this.input_blur(evt); + }); + this.search_field.observe("keyup", function(evt) { + return _this.keyup_checker(evt); + }); + this.search_field.observe("keydown", function(evt) { + return _this.keydown_checker(evt); + }); + this.search_field.observe("focus", function(evt) { + return _this.input_focus(evt); + }); + if (this.is_multiple) { + return this.search_choices.observe("click", function(evt) { + return _this.choices_click(evt); + }); + } else { + return this.container.observe("click", function(evt) { + return evt.preventDefault(); + }); + } + }; + + Chosen.prototype.destroy = function() { + document.stopObserving("click", this.click_test_action); + this.form_field.stopObserving(); + this.container.stopObserving(); + this.search_results.stopObserving(); + this.search_field.stopObserving(); + if (this.form_field_label != null) { + this.form_field_label.stopObserving(); + } + if (this.is_multiple) { + this.search_choices.stopObserving(); + this.container.select(".search-choice-close").each(function(choice) { + return choice.stopObserving(); + }); + } else { + this.selected_item.stopObserving(); + } + if (this.search_field.tabIndex) { + this.form_field.tabIndex = this.search_field.tabIndex; + } + this.container.remove(); + return this.form_field.show(); + }; + + Chosen.prototype.search_field_disabled = function() { + this.is_disabled = this.form_field.disabled; + if (this.is_disabled) { + this.container.addClassName('chosen-disabled'); + this.search_field.disabled = true; + if (!this.is_multiple) { + this.selected_item.stopObserving("focus", this.activate_action); + } + return this.close_field(); + } else { + this.container.removeClassName('chosen-disabled'); + this.search_field.disabled = false; + if (!this.is_multiple) { + return this.selected_item.observe("focus", this.activate_action); + } + } + }; + + Chosen.prototype.container_mousedown = function(evt) { + if (!this.is_disabled) { + if (evt && evt.type === "mousedown" && !this.results_showing) { + evt.stop(); + } + if (!((evt != null) && evt.target.hasClassName("search-choice-close"))) { + if (!this.active_field) { + if (this.is_multiple) { + this.search_field.clear(); + } + document.observe("click", this.click_test_action); + this.results_show(); + } else if (!this.is_multiple && evt && (evt.target === this.selected_item || evt.target.up("a.chosen-single"))) { + this.results_toggle(); + } + return this.activate_field(); + } + } + }; + + Chosen.prototype.container_mouseup = function(evt) { + if (evt.target.nodeName === "ABBR" && !this.is_disabled) { + return this.results_reset(evt); + } + }; + + Chosen.prototype.search_results_mousewheel = function(evt) { + var delta; + + delta = -evt.wheelDelta || evt.detail; + if (delta != null) { + evt.preventDefault(); + if (evt.type === 'DOMMouseScroll') { + delta = delta * 40; + } + return this.search_results.scrollTop = delta + this.search_results.scrollTop; + } + }; + + Chosen.prototype.blur_test = function(evt) { + if (!this.active_field && this.container.hasClassName("chosen-container-active")) { + return this.close_field(); + } + }; + + Chosen.prototype.close_field = function() { + document.stopObserving("click", this.click_test_action); + this.active_field = false; + this.results_hide(); + this.container.removeClassName("chosen-container-active"); + this.clear_backstroke(); + this.show_search_field_default(); + return this.search_field_scale(); + }; + + Chosen.prototype.activate_field = function() { + this.container.addClassName("chosen-container-active"); + this.active_field = true; + this.search_field.value = this.search_field.value; + return this.search_field.focus(); + }; + + Chosen.prototype.test_active_click = function(evt) { + if (evt.target.up('.chosen-container') === this.container) { + return this.active_field = true; + } else { + return this.close_field(); + } + }; + + Chosen.prototype.results_build = function() { + this.parsing = true; + this.selected_option_count = null; + this.results_data = SelectParser.select_to_array(this.form_field); + if (this.is_multiple) { + this.search_choices.select("li.search-choice").invoke("remove"); + } else if (!this.is_multiple) { + this.single_set_selected_text(); + if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) { + this.search_field.readOnly = true; + this.container.addClassName("chosen-container-single-nosearch"); + } else { + this.search_field.readOnly = false; + this.container.removeClassName("chosen-container-single-nosearch"); + } + } + this.update_results_content(this.results_option_build({ + first: true + })); + this.search_field_disabled(); + this.show_search_field_default(); + this.search_field_scale(); + return this.parsing = false; + }; + + Chosen.prototype.result_do_highlight = function(el) { + var high_bottom, high_top, maxHeight, visible_bottom, visible_top; + + this.result_clear_highlight(); + this.result_highlight = el; + this.result_highlight.addClassName("highlighted"); + maxHeight = parseInt(this.search_results.getStyle('maxHeight'), 10); + visible_top = this.search_results.scrollTop; + visible_bottom = maxHeight + visible_top; + high_top = this.result_highlight.positionedOffset().top; + high_bottom = high_top + this.result_highlight.getHeight(); + if (high_bottom >= visible_bottom) { + return this.search_results.scrollTop = (high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0; + } else if (high_top < visible_top) { + return this.search_results.scrollTop = high_top; + } + }; + + Chosen.prototype.result_clear_highlight = function() { + if (this.result_highlight) { + this.result_highlight.removeClassName('highlighted'); + } + return this.result_highlight = null; + }; + + Chosen.prototype.results_show = function() { + if (this.is_multiple && this.max_selected_options <= this.choices_count()) { + this.form_field.fire("chosen:maxselected", { + chosen: this + }); + return false; + } + this.container.addClassName("chosen-with-drop"); + this.form_field.fire("chosen:showing_dropdown", { + chosen: this + }); + this.results_showing = true; + this.search_field.focus(); + this.search_field.value = this.search_field.value; + return this.winnow_results(); + }; + + Chosen.prototype.update_results_content = function(content) { + return this.search_results.update(content); + }; + + Chosen.prototype.results_hide = function() { + if (this.results_showing) { + this.result_clear_highlight(); + this.container.removeClassName("chosen-with-drop"); + this.form_field.fire("chosen:hiding_dropdown", { + chosen: this + }); + } + return this.results_showing = false; + }; + + Chosen.prototype.set_tab_index = function(el) { + var ti; + + if (this.form_field.tabIndex) { + ti = this.form_field.tabIndex; + this.form_field.tabIndex = -1; + return this.search_field.tabIndex = ti; + } + }; + + Chosen.prototype.set_label_behavior = function() { + var _this = this; + + this.form_field_label = this.form_field.up("label"); + if (this.form_field_label == null) { + this.form_field_label = $$("label[for='" + this.form_field.id + "']").first(); + } + if (this.form_field_label != null) { + return this.form_field_label.observe("click", function(evt) { + if (_this.is_multiple) { + return _this.container_mousedown(evt); + } else { + return _this.activate_field(); + } + }); + } + }; + + Chosen.prototype.show_search_field_default = function() { + if (this.is_multiple && this.choices_count() < 1 && !this.active_field) { + this.search_field.value = this.default_text; + return this.search_field.addClassName("default"); + } else { + this.search_field.value = ""; + return this.search_field.removeClassName("default"); + } + }; + + Chosen.prototype.search_results_mouseup = function(evt) { + var target; + + target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); + if (target) { + this.result_highlight = target; + this.result_select(evt); + return this.search_field.focus(); + } + }; + + Chosen.prototype.search_results_mouseover = function(evt) { + var target; + + target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); + if (target) { + return this.result_do_highlight(target); + } + }; + + Chosen.prototype.search_results_mouseout = function(evt) { + if (evt.target.hasClassName('active-result') || evt.target.up('.active-result')) { + return this.result_clear_highlight(); + } + }; + + Chosen.prototype.choice_build = function(item) { + var choice, close_link, + _this = this; + + choice = new Element('li', { + "class": "search-choice" + }).update("" + item.html + ""); + if (item.disabled) { + choice.addClassName('search-choice-disabled'); + } else { + close_link = new Element('a', { + href: '#', + "class": 'search-choice-close', + rel: item.array_index + }); + close_link.observe("click", function(evt) { + return _this.choice_destroy_link_click(evt); + }); + choice.insert(close_link); + } + return this.search_container.insert({ + before: choice + }); + }; + + Chosen.prototype.choice_destroy_link_click = function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + if (!this.is_disabled) { + return this.choice_destroy(evt.target); + } + }; + + Chosen.prototype.choice_destroy = function(link) { + if (this.result_deselect(link.readAttribute("rel"))) { + this.show_search_field_default(); + if (this.is_multiple && this.choices_count() > 0 && this.search_field.value.length < 1) { + this.results_hide(); + } + link.up('li').remove(); + return this.search_field_scale(); + } + }; + + Chosen.prototype.results_reset = function() { + this.form_field.options[0].selected = true; + this.selected_option_count = null; + this.single_set_selected_text(); + this.show_search_field_default(); + this.results_reset_cleanup(); + if (typeof Event.simulate === 'function') { + this.form_field.simulate("change"); + } + if (this.active_field) { + return this.results_hide(); + } + }; + + Chosen.prototype.results_reset_cleanup = function() { + var deselect_trigger; + + this.current_selectedIndex = this.form_field.selectedIndex; + deselect_trigger = this.selected_item.down("abbr"); + if (deselect_trigger) { + return deselect_trigger.remove(); + } + }; + + Chosen.prototype.result_select = function(evt) { + var high, item, selected_index; + + if (this.result_highlight) { + high = this.result_highlight; + this.result_clear_highlight(); + if (this.is_multiple && this.max_selected_options <= this.choices_count()) { + this.form_field.fire("chosen:maxselected", { + chosen: this + }); + return false; + } + if (this.is_multiple) { + high.removeClassName("active-result"); + } else { + if (this.result_single_selected) { + this.result_single_selected.removeClassName("result-selected"); + selected_index = this.result_single_selected.getAttribute('data-option-array-index'); + this.results_data[selected_index].selected = false; + } + this.result_single_selected = high; + } + high.addClassName("result-selected"); + item = this.results_data[high.getAttribute("data-option-array-index")]; + item.selected = true; + this.form_field.options[item.options_index].selected = true; + this.selected_option_count = null; + if (this.is_multiple) { + this.choice_build(item); + } else { + this.single_set_selected_text(item.text); + } + if (!((evt.metaKey || evt.ctrlKey) && this.is_multiple)) { + this.results_hide(); + } + this.search_field.value = ""; + if (typeof Event.simulate === 'function' && (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex)) { + this.form_field.simulate("change"); + } + this.current_selectedIndex = this.form_field.selectedIndex; + return this.search_field_scale(); + } + }; + + Chosen.prototype.single_set_selected_text = function(text) { + if (text == null) { + text = this.default_text; + } + if (text === this.default_text) { + this.selected_item.addClassName("chosen-default"); + } else { + this.single_deselect_control_build(); + this.selected_item.removeClassName("chosen-default"); + } + return this.selected_item.down("span").update(text); + }; + + Chosen.prototype.result_deselect = function(pos) { + var result_data; + + result_data = this.results_data[pos]; + if (!this.form_field.options[result_data.options_index].disabled) { + result_data.selected = false; + this.form_field.options[result_data.options_index].selected = false; + this.selected_option_count = null; + this.result_clear_highlight(); + if (this.results_showing) { + this.winnow_results(); + } + if (typeof Event.simulate === 'function') { + this.form_field.simulate("change"); + } + this.search_field_scale(); + return true; + } else { + return false; + } + }; + + Chosen.prototype.single_deselect_control_build = function() { + if (!this.allow_single_deselect) { + return; + } + if (!this.selected_item.down("abbr")) { + this.selected_item.down("span").insert({ + after: "" + }); + } + return this.selected_item.addClassName("chosen-single-with-deselect"); + }; + + Chosen.prototype.get_search_text = function() { + if (this.search_field.value === this.default_text) { + return ""; + } else { + return this.search_field.value.strip().escapeHTML(); + } + }; + + Chosen.prototype.winnow_results_set_highlight = function() { + var do_high; + + if (!this.is_multiple) { + do_high = this.search_results.down(".result-selected.active-result"); + } + if (do_high == null) { + do_high = this.search_results.down(".active-result"); + } + if (do_high != null) { + return this.result_do_highlight(do_high); + } + }; + + Chosen.prototype.no_results = function(terms) { + return this.search_results.insert(this.no_results_temp.evaluate({ + terms: terms + })); + }; + + Chosen.prototype.no_results_clear = function() { + var nr, _results; + + nr = null; + _results = []; + while (nr = this.search_results.down(".no-results")) { + _results.push(nr.remove()); + } + return _results; + }; + + Chosen.prototype.keydown_arrow = function() { + var next_sib; + + if (this.results_showing && this.result_highlight) { + next_sib = this.result_highlight.next('.active-result'); + if (next_sib) { + return this.result_do_highlight(next_sib); + } + } else { + return this.results_show(); + } + }; + + Chosen.prototype.keyup_arrow = function() { + var actives, prevs, sibs; + + if (!this.results_showing && !this.is_multiple) { + return this.results_show(); + } else if (this.result_highlight) { + sibs = this.result_highlight.previousSiblings(); + actives = this.search_results.select("li.active-result"); + prevs = sibs.intersect(actives); + if (prevs.length) { + return this.result_do_highlight(prevs.first()); + } else { + if (this.choices_count() > 0) { + this.results_hide(); + } + return this.result_clear_highlight(); + } + } + }; + + Chosen.prototype.keydown_backstroke = function() { + var next_available_destroy; + + if (this.pending_backstroke) { + this.choice_destroy(this.pending_backstroke.down("a")); + return this.clear_backstroke(); + } else { + next_available_destroy = this.search_container.siblings().last(); + if (next_available_destroy && next_available_destroy.hasClassName("search-choice") && !next_available_destroy.hasClassName("search-choice-disabled")) { + this.pending_backstroke = next_available_destroy; + if (this.pending_backstroke) { + this.pending_backstroke.addClassName("search-choice-focus"); + } + if (this.single_backstroke_delete) { + return this.keydown_backstroke(); + } else { + return this.pending_backstroke.addClassName("search-choice-focus"); + } + } + } + }; + + Chosen.prototype.clear_backstroke = function() { + if (this.pending_backstroke) { + this.pending_backstroke.removeClassName("search-choice-focus"); + } + return this.pending_backstroke = null; + }; + + Chosen.prototype.keydown_checker = function(evt) { + var stroke, _ref1; + + stroke = (_ref1 = evt.which) != null ? _ref1 : evt.keyCode; + this.search_field_scale(); + if (stroke !== 8 && this.pending_backstroke) { + this.clear_backstroke(); + } + switch (stroke) { + case 8: + this.backstroke_length = this.search_field.value.length; + break; + case 9: + if (this.results_showing && !this.is_multiple) { + this.result_select(evt); + } + this.mouse_on_container = false; + break; + case 13: + evt.preventDefault(); + break; + case 38: + evt.preventDefault(); + this.keyup_arrow(); + break; + case 40: + evt.preventDefault(); + this.keydown_arrow(); + break; + } + }; + + Chosen.prototype.search_field_scale = function() { + var div, f_width, h, style, style_block, styles, w, _i, _len; + + if (this.is_multiple) { + h = 0; + w = 0; + style_block = "position:absolute; left: -1000px; top: -1000px; display:none;"; + styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing']; + for (_i = 0, _len = styles.length; _i < _len; _i++) { + style = styles[_i]; + style_block += style + ":" + this.search_field.getStyle(style) + ";"; + } + div = new Element('div', { + 'style': style_block + }).update(this.search_field.value.escapeHTML()); + document.body.appendChild(div); + w = Element.measure(div, 'width') + 25; + div.remove(); + f_width = this.container.getWidth(); + if (w > f_width - 10) { + w = f_width - 10; + } + return this.search_field.setStyle({ + 'width': w + 'px' + }); + } + }; + + return Chosen; + + })(AbstractChosen); + +}).call(this); diff --git a/js/ebanx/chosen/chosen.proto.min.js b/js/ebanx/chosen/chosen.proto.min.js new file mode 100644 index 00000000..937688fc --- /dev/null +++ b/js/ebanx/chosen/chosen.proto.min.js @@ -0,0 +1,2 @@ +/* Chosen v1.0.0 | (c) 2011-2013 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ +!function(){var AbstractChosen,SelectParser,a,b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(a){return"OPTGROUP"===a.nodeName.toUpperCase()?this.add_group(a):this.add_option(a)},SelectParser.prototype.add_group=function(a){var b,c,d,e,f,g;for(b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:this.escapeExpression(a.label),children:0,disabled:a.disabled}),f=a.childNodes,g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},SelectParser.prototype.add_option=function(a,b,c){return"OPTION"===a.nodeName.toUpperCase()?(""!==a.text?(null!=b&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1):void 0},SelectParser.prototype.escapeExpression=function(a){var b,c;return null==a||a===!1?"":/[\&\<\>\"\'\`]/.test(a)?(b={"<":"<",">":">",'"':""","'":"'","`":"`"},c=/&(?!\w+;)|[\<\>\"\'\`]/g,a.replace(c,function(a){return b[a]||"&"})):a},SelectParser}(),SelectParser.select_to_array=function(a){var b,c,d,e,f;for(c=new SelectParser,f=a.childNodes,d=0,e=f.length;e>d;d++)b=f[d],c.add_node(b);return c.parsed},AbstractChosen=function(){function AbstractChosen(a,b){this.form_field=a,this.options=null!=b?b:{},AbstractChosen.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers())}return AbstractChosen.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null,this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null!=this.options.enable_split_word_search?this.options.enable_split_word_search:!0,this.group_search=null!=this.options.group_search?this.options.group_search:!0,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null!=this.options.single_backstroke_delete?this.options.single_backstroke_delete:!0,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null!=this.options.display_selected_options?this.options.display_selected_options:!0,this.display_disabled_options=null!=this.options.display_disabled_options?this.options.display_disabled_options:!0},AbstractChosen.prototype.set_default_text=function(){return this.default_text=this.form_field.getAttribute("data-placeholder")?this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.options.placeholder_text_multiple||this.options.placeholder_text||AbstractChosen.default_multiple_text:this.options.placeholder_text_single||this.options.placeholder_text||AbstractChosen.default_single_text,this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||AbstractChosen.default_no_result_text},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(){var a=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return a.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},AbstractChosen.prototype.input_blur=function(){var a=this;return this.mouse_on_container?void 0:(this.active_field=!1,setTimeout(function(){return a.blur_test()},100))},AbstractChosen.prototype.results_option_build=function(a){var b,c,d,e,f;for(b="",f=this.results_data,d=0,e=f.length;e>d;d++)c=f[d],b+=c.group?this.result_add_group(c):this.result_add_option(c),(null!=a?a.first:void 0)&&(c.selected&&this.is_multiple?this.choice_build(c):c.selected&&!this.is_multiple&&this.single_set_selected_text(c.text));return b},AbstractChosen.prototype.result_add_option=function(a){var b,c;return a.search_match?this.include_option_in_results(a)?(b=[],a.disabled||a.selected&&this.is_multiple||b.push("active-result"),!a.disabled||a.selected&&this.is_multiple||b.push("disabled-result"),a.selected&&b.push("result-selected"),null!=a.group_array_index&&b.push("group-option"),""!==a.classes&&b.push(a.classes),c=""!==a.style.cssText?' style="'+a.style+'"':"",'
  • '+a.search_text+"
  • "):"":""},AbstractChosen.prototype.result_add_group=function(a){return a.search_match||a.group_match?a.active_options>0?'
  • '+a.search_text+"
  • ":"":""},AbstractChosen.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.result_single_selected=null,this.results_build(),this.results_showing?this.winnow_results():void 0},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m;for(this.no_results_clear(),e=0,g=this.get_search_text(),a=g.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),d=this.search_contains?"":"^",c=new RegExp(d+a,"i"),j=new RegExp(a,"i"),m=this.results_data,k=0,l=m.length;l>k;k++)b=m[k],b.search_match=!1,f=null,this.include_option_in_results(b)&&(b.group&&(b.group_match=!1,b.active_options=0),null!=b.group_array_index&&this.results_data[b.group_array_index]&&(f=this.results_data[b.group_array_index],0===f.active_options&&f.search_match&&(e+=1),f.active_options+=1),(!b.group||this.group_search)&&(b.search_text=b.group?b.label:b.html,b.search_match=this.search_string_match(b.search_text,c),b.search_match&&!b.group&&(e+=1),b.search_match?(g.length&&(h=b.search_text.search(j),i=b.search_text.substr(0,h+g.length)+"
    "+b.search_text.substr(h+g.length),b.search_text=i.substr(0,h)+""+i.substr(h)),null!=f&&(f.group_match=!0)):null!=b.group_array_index&&this.results_data[b.group_array_index].search_match&&(b.search_match=!0)));return this.result_clear_highlight(),1>e&&g.length?(this.update_results_content(""),this.no_results(g)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},AbstractChosen.prototype.search_string_match=function(a,b){var c,d,e,f;if(b.test(a))return!0;if(this.enable_split_word_search&&(a.indexOf(" ")>=0||0===a.indexOf("["))&&(d=a.replace(/\[|\]/g,"").split(" "),d.length))for(e=0,f=d.length;f>e;e++)if(c=d[e],b.test(c))return!0},AbstractChosen.prototype.choices_count=function(){var a,b,c,d;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,d=this.form_field.options,b=0,c=d.length;c>b;b++)a=d[b],a.selected&&(this.selected_option_count+=1);return this.selected_option_count},AbstractChosen.prototype.choices_click=function(a){return a.preventDefault(),this.results_showing||this.is_disabled?void 0:this.results_show()},AbstractChosen.prototype.keyup_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:if(a.preventDefault(),this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},AbstractChosen.prototype.container_width=function(){return null!=this.options.width?this.options.width:""+this.form_field.offsetWidth+"px"},AbstractChosen.prototype.include_option_in_results=function(a){return this.is_multiple&&!this.display_selected_options&&a.selected?!1:!this.display_disabled_options&&a.disabled?!1:a.empty?!1:!0},AbstractChosen.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:/iP(od|hone)/i.test(window.navigator.userAgent)?!1:/Android/i.test(window.navigator.userAgent)&&/Mobile/i.test(window.navigator.userAgent)?!1:!0},AbstractChosen.default_multiple_text="Select Some Options",AbstractChosen.default_single_text="Select an Option",AbstractChosen.default_no_result_text="No results match",AbstractChosen}(),this.Chosen=function(b){function Chosen(){return a=Chosen.__super__.constructor.apply(this,arguments)}return c(Chosen,b),Chosen.prototype.setup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.is_rtl=this.form_field.hasClassName("chosen-rtl")},Chosen.prototype.set_default_values=function(){return Chosen.__super__.set_default_values.call(this),this.single_temp=new Template('#{default}
    '),this.multi_temp=new Template('
    '),this.no_results_temp=new Template('
  • '+this.results_none_found+' "#{terms}"
  • ')},Chosen.prototype.set_up_html=function(){var a,b;return a=["chosen-container"],a.push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&a.push(this.form_field.className),this.is_rtl&&a.push("chosen-rtl"),b={"class":a.join(" "),style:"width: "+this.container_width()+";",title:this.form_field.title},this.form_field.id.length&&(b.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=this.is_multiple?new Element("div",b).update(this.multi_temp.evaluate({"default":this.default_text})):new Element("div",b).update(this.single_temp.evaluate({"default":this.default_text})),this.form_field.hide().insert({after:this.container}),this.dropdown=this.container.down("div.chosen-drop"),this.search_field=this.container.down("input"),this.search_results=this.container.down("ul.chosen-results"),this.search_field_scale(),this.search_no_results=this.container.down("li.no-results"),this.is_multiple?(this.search_choices=this.container.down("ul.chosen-choices"),this.search_container=this.container.down("li.search-field")):(this.search_container=this.container.down("div.chosen-search"),this.selected_item=this.container.down(".chosen-single")),this.results_build(),this.set_tab_index(),this.set_label_behavior(),this.form_field.fire("chosen:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var a=this;return this.container.observe("mousedown",function(b){return a.container_mousedown(b)}),this.container.observe("mouseup",function(b){return a.container_mouseup(b)}),this.container.observe("mouseenter",function(b){return a.mouse_enter(b)}),this.container.observe("mouseleave",function(b){return a.mouse_leave(b)}),this.search_results.observe("mouseup",function(b){return a.search_results_mouseup(b)}),this.search_results.observe("mouseover",function(b){return a.search_results_mouseover(b)}),this.search_results.observe("mouseout",function(b){return a.search_results_mouseout(b)}),this.search_results.observe("mousewheel",function(b){return a.search_results_mousewheel(b)}),this.search_results.observe("DOMMouseScroll",function(b){return a.search_results_mousewheel(b)}),this.form_field.observe("chosen:updated",function(b){return a.results_update_field(b)}),this.form_field.observe("chosen:activate",function(b){return a.activate_field(b)}),this.form_field.observe("chosen:open",function(b){return a.container_mousedown(b)}),this.search_field.observe("blur",function(b){return a.input_blur(b)}),this.search_field.observe("keyup",function(b){return a.keyup_checker(b)}),this.search_field.observe("keydown",function(b){return a.keydown_checker(b)}),this.search_field.observe("focus",function(b){return a.input_focus(b)}),this.is_multiple?this.search_choices.observe("click",function(b){return a.choices_click(b)}):this.container.observe("click",function(a){return a.preventDefault()})},Chosen.prototype.destroy=function(){return document.stopObserving("click",this.click_test_action),this.form_field.stopObserving(),this.container.stopObserving(),this.search_results.stopObserving(),this.search_field.stopObserving(),null!=this.form_field_label&&this.form_field_label.stopObserving(),this.is_multiple?(this.search_choices.stopObserving(),this.container.select(".search-choice-close").each(function(a){return a.stopObserving()})):this.selected_item.stopObserving(),this.search_field.tabIndex&&(this.form_field.tabIndex=this.search_field.tabIndex),this.container.remove(),this.form_field.show()},Chosen.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field.disabled,this.is_disabled?(this.container.addClassName("chosen-disabled"),this.search_field.disabled=!0,this.is_multiple||this.selected_item.stopObserving("focus",this.activate_action),this.close_field()):(this.container.removeClassName("chosen-disabled"),this.search_field.disabled=!1,this.is_multiple?void 0:this.selected_item.observe("focus",this.activate_action))},Chosen.prototype.container_mousedown=function(a){return this.is_disabled||(a&&"mousedown"===a.type&&!this.results_showing&&a.stop(),null!=a&&a.target.hasClassName("search-choice-close"))?void 0:(this.active_field?this.is_multiple||!a||a.target!==this.selected_item&&!a.target.up("a.chosen-single")||this.results_toggle():(this.is_multiple&&this.search_field.clear(),document.observe("click",this.click_test_action),this.results_show()),this.activate_field())},Chosen.prototype.container_mouseup=function(a){return"ABBR"!==a.target.nodeName||this.is_disabled?void 0:this.results_reset(a)},Chosen.prototype.search_results_mousewheel=function(a){var b;return b=-a.wheelDelta||a.detail,null!=b?(a.preventDefault(),"DOMMouseScroll"===a.type&&(b=40*b),this.search_results.scrollTop=b+this.search_results.scrollTop):void 0},Chosen.prototype.blur_test=function(){return!this.active_field&&this.container.hasClassName("chosen-container-active")?this.close_field():void 0},Chosen.prototype.close_field=function(){return document.stopObserving("click",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClassName("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return this.container.addClassName("chosen-container-active"),this.active_field=!0,this.search_field.value=this.search_field.value,this.search_field.focus()},Chosen.prototype.test_active_click=function(a){return a.target.up(".chosen-container")===this.container?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=SelectParser.select_to_array(this.form_field),this.is_multiple?this.search_choices.select("li.search-choice").invoke("remove"):this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field.readOnly=!0,this.container.addClassName("chosen-container-single-nosearch")):(this.search_field.readOnly=!1,this.container.removeClassName("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},Chosen.prototype.result_do_highlight=function(a){var b,c,d,e,f;return this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClassName("highlighted"),d=parseInt(this.search_results.getStyle("maxHeight"),10),f=this.search_results.scrollTop,e=d+f,c=this.result_highlight.positionedOffset().top,b=c+this.result_highlight.getHeight(),b>=e?this.search_results.scrollTop=b-d>0?b-d:0:f>c?this.search_results.scrollTop=c:void 0},Chosen.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClassName("highlighted"),this.result_highlight=null},Chosen.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field.fire("chosen:maxselected",{chosen:this}),!1):(this.container.addClassName("chosen-with-drop"),this.form_field.fire("chosen:showing_dropdown",{chosen:this}),this.results_showing=!0,this.search_field.focus(),this.search_field.value=this.search_field.value,this.winnow_results())},Chosen.prototype.update_results_content=function(a){return this.search_results.update(a)},Chosen.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClassName("chosen-with-drop"),this.form_field.fire("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},Chosen.prototype.set_tab_index=function(){var a;return this.form_field.tabIndex?(a=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field.tabIndex=a):void 0},Chosen.prototype.set_label_behavior=function(){var a=this;return this.form_field_label=this.form_field.up("label"),null==this.form_field_label&&(this.form_field_label=$$("label[for='"+this.form_field.id+"']").first()),null!=this.form_field_label?this.form_field_label.observe("click",function(b){return a.is_multiple?a.container_mousedown(b):a.activate_field()}):void 0},Chosen.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.value=this.default_text,this.search_field.addClassName("default")):(this.search_field.value="",this.search_field.removeClassName("default"))},Chosen.prototype.search_results_mouseup=function(a){var b;return b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result"),b?(this.result_highlight=b,this.result_select(a),this.search_field.focus()):void 0},Chosen.prototype.search_results_mouseover=function(a){var b;return b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result"),b?this.result_do_highlight(b):void 0},Chosen.prototype.search_results_mouseout=function(a){return a.target.hasClassName("active-result")||a.target.up(".active-result")?this.result_clear_highlight():void 0},Chosen.prototype.choice_build=function(a){var b,c,d=this;return b=new Element("li",{"class":"search-choice"}).update(""+a.html+""),a.disabled?b.addClassName("search-choice-disabled"):(c=new Element("a",{href:"#","class":"search-choice-close",rel:a.array_index}),c.observe("click",function(a){return d.choice_destroy_link_click(a)}),b.insert(c)),this.search_container.insert({before:b})},Chosen.prototype.choice_destroy_link_click=function(a){return a.preventDefault(),a.stopPropagation(),this.is_disabled?void 0:this.choice_destroy(a.target)},Chosen.prototype.choice_destroy=function(a){return this.result_deselect(a.readAttribute("rel"))?(this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.search_field.value.length<1&&this.results_hide(),a.up("li").remove(),this.search_field_scale()):void 0},Chosen.prototype.results_reset=function(){return this.form_field.options[0].selected=!0,this.selected_option_count=null,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),"function"==typeof Event.simulate&&this.form_field.simulate("change"),this.active_field?this.results_hide():void 0},Chosen.prototype.results_reset_cleanup=function(){var a;return this.current_selectedIndex=this.form_field.selectedIndex,a=this.selected_item.down("abbr"),a?a.remove():void 0},Chosen.prototype.result_select=function(a){var b,c,d;return this.result_highlight?(b=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field.fire("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?b.removeClassName("active-result"):(this.result_single_selected&&(this.result_single_selected.removeClassName("result-selected"),d=this.result_single_selected.getAttribute("data-option-array-index"),this.results_data[d].selected=!1),this.result_single_selected=b),b.addClassName("result-selected"),c=this.results_data[b.getAttribute("data-option-array-index")],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(c):this.single_set_selected_text(c.text),(a.metaKey||a.ctrlKey)&&this.is_multiple||this.results_hide(),this.search_field.value="","function"!=typeof Event.simulate||!this.is_multiple&&this.form_field.selectedIndex===this.current_selectedIndex||this.form_field.simulate("change"),this.current_selectedIndex=this.form_field.selectedIndex,this.search_field_scale())):void 0},Chosen.prototype.single_set_selected_text=function(a){return null==a&&(a=this.default_text),a===this.default_text?this.selected_item.addClassName("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClassName("chosen-default")),this.selected_item.down("span").update(a)},Chosen.prototype.result_deselect=function(a){var b;return b=this.results_data[a],this.form_field.options[b.options_index].disabled?!1:(b.selected=!1,this.form_field.options[b.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),"function"==typeof Event.simulate&&this.form_field.simulate("change"),this.search_field_scale(),!0)},Chosen.prototype.single_deselect_control_build=function(){return this.allow_single_deselect?(this.selected_item.down("abbr")||this.selected_item.down("span").insert({after:''}),this.selected_item.addClassName("chosen-single-with-deselect")):void 0},Chosen.prototype.get_search_text=function(){return this.search_field.value===this.default_text?"":this.search_field.value.strip().escapeHTML()},Chosen.prototype.winnow_results_set_highlight=function(){var a;return this.is_multiple||(a=this.search_results.down(".result-selected.active-result")),null==a&&(a=this.search_results.down(".active-result")),null!=a?this.result_do_highlight(a):void 0},Chosen.prototype.no_results=function(a){return this.search_results.insert(this.no_results_temp.evaluate({terms:a}))},Chosen.prototype.no_results_clear=function(){var a,b;for(a=null,b=[];a=this.search_results.down(".no-results");)b.push(a.remove());return b},Chosen.prototype.keydown_arrow=function(){var a;return this.results_showing&&this.result_highlight?(a=this.result_highlight.next(".active-result"))?this.result_do_highlight(a):void 0:this.results_show()},Chosen.prototype.keyup_arrow=function(){var a,b,c;return this.results_showing||this.is_multiple?this.result_highlight?(c=this.result_highlight.previousSiblings(),a=this.search_results.select("li.active-result"),b=c.intersect(a),b.length?this.result_do_highlight(b.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},Chosen.prototype.keydown_backstroke=function(){var a;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.down("a")),this.clear_backstroke()):(a=this.search_container.siblings().last(),a&&a.hasClassName("search-choice")&&!a.hasClassName("search-choice-disabled")?(this.pending_backstroke=a,this.pending_backstroke&&this.pending_backstroke.addClassName("search-choice-focus"),this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClassName("search-choice-focus")):void 0)},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClassName("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),8!==b&&this.pending_backstroke&&this.clear_backstroke(),b){case 8:this.backstroke_length=this.search_field.value.length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:a.preventDefault(),this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var a,b,c,d,e,f,g,h,i;if(this.is_multiple){for(c=0,g=0,e="position:absolute; left: -1000px; top: -1000px; display:none;",f=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"],h=0,i=f.length;i>h;h++)d=f[h],e+=d+":"+this.search_field.getStyle(d)+";";return a=new Element("div",{style:e}).update(this.search_field.value.escapeHTML()),document.body.appendChild(a),g=Element.measure(a,"width")+25,a.remove(),b=this.container.getWidth(),g>b-10&&(g=b-10),this.search_field.setStyle({width:g+"px"})}},Chosen}(AbstractChosen)}.call(this); \ No newline at end of file diff --git a/js/ebanx/clipboard/clipboard.js b/js/ebanx/clipboard/clipboard.js new file mode 100644 index 00000000..2bd07455 --- /dev/null +++ b/js/ebanx/clipboard/clipboard.js @@ -0,0 +1,823 @@ +/*! + * clipboard.js v1.6.1 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +(function (f) { + if (typeof exports === "object" && typeof module !== "undefined") { + module.exports = f() + } else if (typeof define === "function" && define.amd) { + define([], f) + } else { + var g; + if (typeof window !== "undefined") { + g = window + } else if (typeof global !== "undefined") { + g = global + } else if (typeof self !== "undefined") { + g = self + } else { + g = this + } + g.Clipboard = f() + } +})(function () { + var define, module, exports; + return (function e(t, n, r) { + function s(o, u) { + if (!n[o]) { + if (!t[o]) { + var a = typeof require == "function" && require; + if (!u && a)return a(o, !0); + if (i)return i(o, !0); + var f = new Error("Cannot find module '" + o + "'"); + throw f.code = "MODULE_NOT_FOUND", f + } + var l = n[o] = {exports: {}}; + t[o][0].call(l.exports, function (e) { + var n = t[o][1][e]; + return s(n ? n : e) + }, l, l.exports, e, t, n, r) + } + return n[o].exports + } + + var i = typeof require == "function" && require; + for (var o = 0; o < r.length; o++)s(r[o]); + return s + })({ + 1: [function (require, module, exports) { + var DOCUMENT_NODE_TYPE = 9; + + /** + * A polyfill for Element.matches() + */ + if (typeof Element !== 'undefined' && !Element.prototype.matches) { + var proto = Element.prototype; + + proto.matches = proto.matchesSelector || + proto.mozMatchesSelector || + proto.msMatchesSelector || + proto.oMatchesSelector || + proto.webkitMatchesSelector; + } + + /** + * Finds the closest parent that matches a selector. + * + * @param {Element} element + * @param {String} selector + * @return {Function} + */ + function closest(element, selector) { + while (element && element.nodeType !== DOCUMENT_NODE_TYPE) { + if (element.matches(selector)) return element; + element = element.parentNode; + } + } + + module.exports = closest; + + }, {}], 2: [function (require, module, exports) { + var closest = require('./closest'); + + /** + * Delegates event to a selector. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @param {Boolean} useCapture + * @return {Object} + */ + function delegate(element, selector, type, callback, useCapture) { + var listenerFn = listener.apply(this, arguments); + + element.addEventListener(type, listenerFn, useCapture); + + return { + destroy: function () { + element.removeEventListener(type, listenerFn, useCapture); + } + } + } + + /** + * Finds closest match and invokes callback. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Function} + */ + function listener(element, selector, type, callback) { + return function (e) { + e.delegateTarget = closest(e.target, selector); + + if (e.delegateTarget) { + callback.call(element, e); + } + } + } + + module.exports = delegate; + + }, {"./closest": 1}], 3: [function (require, module, exports) { + /** + * Check if argument is a HTML element. + * + * @param {Object} value + * @return {Boolean} + */ + exports.node = function (value) { + return value !== undefined + && value instanceof HTMLElement + && value.nodeType === 1; + }; + + /** + * Check if argument is a list of HTML elements. + * + * @param {Object} value + * @return {Boolean} + */ + exports.nodeList = function (value) { + var type = Object.prototype.toString.call(value); + + return value !== undefined + && (type === '[object NodeList]' || type === '[object HTMLCollection]') + && ('length' in value) + && (value.length === 0 || exports.node(value[0])); + }; + + /** + * Check if argument is a string. + * + * @param {Object} value + * @return {Boolean} + */ + exports.string = function (value) { + return typeof value === 'string' + || value instanceof String; + }; + + /** + * Check if argument is a function. + * + * @param {Object} value + * @return {Boolean} + */ + exports.fn = function (value) { + var type = Object.prototype.toString.call(value); + + return type === '[object Function]'; + }; + + }, {}], 4: [function (require, module, exports) { + var is = require('./is'); + var delegate = require('delegate'); + + /** + * Validates all params and calls the right + * listener function based on its target type. + * + * @param {String|HTMLElement|HTMLCollection|NodeList} target + * @param {String} type + * @param {Function} callback + * @return {Object} + */ + function listen(target, type, callback) { + if (!target && !type && !callback) { + throw new Error('Missing required arguments'); + } + + if (!is.string(type)) { + throw new TypeError('Second argument must be a String'); + } + + if (!is.fn(callback)) { + throw new TypeError('Third argument must be a Function'); + } + + if (is.node(target)) { + return listenNode(target, type, callback); + } + else if (is.nodeList(target)) { + return listenNodeList(target, type, callback); + } + else if (is.string(target)) { + return listenSelector(target, type, callback); + } + else { + throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); + } + } + + /** + * Adds an event listener to a HTML element + * and returns a remove listener function. + * + * @param {HTMLElement} node + * @param {String} type + * @param {Function} callback + * @return {Object} + */ + function listenNode(node, type, callback) { + node.addEventListener(type, callback); + + return { + destroy: function () { + node.removeEventListener(type, callback); + } + } + } + + /** + * Add an event listener to a list of HTML elements + * and returns a remove listener function. + * + * @param {NodeList|HTMLCollection} nodeList + * @param {String} type + * @param {Function} callback + * @return {Object} + */ + function listenNodeList(nodeList, type, callback) { + Array.prototype.forEach.call(nodeList, function (node) { + node.addEventListener(type, callback); + }); + + return { + destroy: function () { + Array.prototype.forEach.call(nodeList, function (node) { + node.removeEventListener(type, callback); + }); + } + } + } + + /** + * Add an event listener to a selector + * and returns a remove listener function. + * + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Object} + */ + function listenSelector(selector, type, callback) { + return delegate(document.body, selector, type, callback); + } + + module.exports = listen; + + }, {"./is": 3, "delegate": 2}], 5: [function (require, module, exports) { + function select(element) { + var selectedText; + + if (element.nodeName === 'SELECT') { + element.focus(); + + selectedText = element.value; + } + else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { + var isReadOnly = element.hasAttribute('readonly'); + + if (!isReadOnly) { + element.setAttribute('readonly', ''); + } + + element.select(); + element.setSelectionRange(0, element.value.length); + + if (!isReadOnly) { + element.removeAttribute('readonly'); + } + + selectedText = element.value; + } + else { + if (element.hasAttribute('contenteditable')) { + element.focus(); + } + + var selection = window.getSelection(); + var range = document.createRange(); + + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + + selectedText = selection.toString(); + } + + return selectedText; + } + + module.exports = select; + + }, {}], 6: [function (require, module, exports) { + function E() { + // Keep this empty so it's easier to inherit from + // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) + } + + E.prototype = { + on: function (name, callback, ctx) { + var e = this.e || (this.e = {}); + + (e[name] || (e[name] = [])).push({ + fn: callback, + ctx: ctx + }); + + return this; + }, + + once: function (name, callback, ctx) { + var self = this; + + function listener() { + self.off(name, listener); + callback.apply(ctx, arguments); + }; + + listener._ = callback + return this.on(name, listener, ctx); + }, + + emit: function (name) { + var data = [].slice.call(arguments, 1); + var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); + var i = 0; + var len = evtArr.length; + + for (i; i < len; i++) { + evtArr[i].fn.apply(evtArr[i].ctx, data); + } + + return this; + }, + + off: function (name, callback) { + var e = this.e || (this.e = {}); + var evts = e[name]; + var liveEvents = []; + + if (evts && callback) { + for (var i = 0, len = evts.length; i < len; i++) { + if (evts[i].fn !== callback && evts[i].fn._ !== callback) + liveEvents.push(evts[i]); + } + } + + // Remove event from queue to prevent memory leak + // Suggested by https://github.com/lazd + // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 + + (liveEvents.length) + ? e[name] = liveEvents + : delete e[name]; + + return this; + } + }; + + module.exports = E; + + }, {}], 7: [function (require, module, exports) { + (function (global, factory) { + if (typeof define === "function" && define.amd) { + define(['module', 'select'], factory); + } else if (typeof exports !== "undefined") { + factory(module, require('select')); + } else { + var mod = { + exports: {} + }; + factory(mod, global.select); + global.clipboardAction = mod.exports; + } + })(this, function (module, _select) { + 'use strict'; + + var _select2 = _interopRequireDefault(_select); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + var ClipboardAction = function () { + /** + * @param {Object} options + */ + function ClipboardAction(options) { + _classCallCheck(this, ClipboardAction); + + this.resolveOptions(options); + this.initSelection(); + } + + /** + * Defines base properties passed from constructor. + * @param {Object} options + */ + + + _createClass(ClipboardAction, [{ + key: 'resolveOptions', + value: function resolveOptions() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this.action = options.action; + this.emitter = options.emitter; + this.target = options.target; + this.text = options.text; + this.trigger = options.trigger; + + this.selectedText = ''; + } + }, { + key: 'initSelection', + value: function initSelection() { + if (this.text) { + this.selectFake(); + } else if (this.target) { + this.selectTarget(); + } + } + }, { + key: 'selectFake', + value: function selectFake() { + var _this = this; + + var isRTL = document.documentElement.getAttribute('dir') == 'rtl'; + + this.removeFake(); + + this.fakeHandlerCallback = function () { + return _this.removeFake(); + }; + this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || true; + + this.fakeElem = document.createElement('textarea'); + // Prevent zooming on iOS + this.fakeElem.style.fontSize = '12pt'; + // Reset box model + this.fakeElem.style.border = '0'; + this.fakeElem.style.padding = '0'; + this.fakeElem.style.margin = '0'; + // Move element out of screen horizontally + this.fakeElem.style.position = 'absolute'; + this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; + // Move element to the same position vertically + var yPosition = window.pageYOffset || document.documentElement.scrollTop; + this.fakeElem.style.top = yPosition + 'px'; + + this.fakeElem.setAttribute('readonly', ''); + this.fakeElem.value = this.text; + + document.body.appendChild(this.fakeElem); + + this.selectedText = (0, _select2.default)(this.fakeElem); + this.copyText(); + } + }, { + key: 'removeFake', + value: function removeFake() { + if (this.fakeHandler) { + document.body.removeEventListener('click', this.fakeHandlerCallback); + this.fakeHandler = null; + this.fakeHandlerCallback = null; + } + + if (this.fakeElem) { + document.body.removeChild(this.fakeElem); + this.fakeElem = null; + } + } + }, { + key: 'selectTarget', + value: function selectTarget() { + this.selectedText = (0, _select2.default)(this.target); + this.copyText(); + } + }, { + key: 'copyText', + value: function copyText() { + var succeeded = void 0; + + try { + succeeded = document.execCommand(this.action); + } catch (err) { + succeeded = false; + } + + this.handleResult(succeeded); + } + }, { + key: 'handleResult', + value: function handleResult(succeeded) { + this.emitter.emit(succeeded ? 'success' : 'error', { + action: this.action, + text: this.selectedText, + trigger: this.trigger, + clearSelection: this.clearSelection.bind(this) + }); + } + }, { + key: 'clearSelection', + value: function clearSelection() { + if (this.target) { + this.target.blur(); + } + + window.getSelection().removeAllRanges(); + } + }, { + key: 'destroy', + value: function destroy() { + this.removeFake(); + } + }, { + key: 'action', + set: function set() { + var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy'; + + this._action = action; + + if (this._action !== 'copy' && this._action !== 'cut') { + throw new Error('Invalid "action" value, use either "copy" or "cut"'); + } + }, + get: function get() { + return this._action; + } + }, { + key: 'target', + set: function set(target) { + if (target !== undefined) { + if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) { + if (this.action === 'copy' && target.hasAttribute('disabled')) { + throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); + } + + if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) { + throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); + } + + this._target = target; + } else { + throw new Error('Invalid "target" value, use a valid Element'); + } + } + }, + get: function get() { + return this._target; + } + }]); + + return ClipboardAction; + }(); + + module.exports = ClipboardAction; + }); + + }, {"select": 5}], 8: [function (require, module, exports) { + (function (global, factory) { + if (typeof define === "function" && define.amd) { + define(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], factory); + } else if (typeof exports !== "undefined") { + factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener')); + } else { + var mod = { + exports: {} + }; + factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener); + global.clipboard = mod.exports; + } + })(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) { + 'use strict'; + + var _clipboardAction2 = _interopRequireDefault(_clipboardAction); + + var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter); + + var _goodListener2 = _interopRequireDefault(_goodListener); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + function _possibleConstructorReturn(self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; + } + + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; + } + + var Clipboard = function (_Emitter) { + _inherits(Clipboard, _Emitter); + + /** + * @param {String|HTMLElement|HTMLCollection|NodeList} trigger + * @param {Object} options + */ + function Clipboard(trigger, options) { + _classCallCheck(this, Clipboard); + + var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this)); + + _this.resolveOptions(options); + _this.listenClick(trigger); + return _this; + } + + /** + * Defines if attributes would be resolved using internal setter functions + * or custom functions that were passed in the constructor. + * @param {Object} options + */ + + + _createClass(Clipboard, [{ + key: 'resolveOptions', + value: function resolveOptions() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this.action = typeof options.action === 'function' ? options.action : this.defaultAction; + this.target = typeof options.target === 'function' ? options.target : this.defaultTarget; + this.text = typeof options.text === 'function' ? options.text : this.defaultText; + } + }, { + key: 'listenClick', + value: function listenClick(trigger) { + var _this2 = this; + + this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) { + return _this2.onClick(e); + }); + } + }, { + key: 'onClick', + value: function onClick(e) { + var trigger = e.delegateTarget || e.currentTarget; + + if (this.clipboardAction) { + this.clipboardAction = null; + } + + this.clipboardAction = new _clipboardAction2.default({ + action: this.action(trigger), + target: this.target(trigger), + text: this.text(trigger), + trigger: trigger, + emitter: this + }); + } + }, { + key: 'defaultAction', + value: function defaultAction(trigger) { + return getAttributeValue('action', trigger); + } + }, { + key: 'defaultTarget', + value: function defaultTarget(trigger) { + var selector = getAttributeValue('target', trigger); + + if (selector) { + return document.querySelector(selector); + } + } + }, { + key: 'defaultText', + value: function defaultText(trigger) { + return getAttributeValue('text', trigger); + } + }, { + key: 'destroy', + value: function destroy() { + this.listener.destroy(); + + if (this.clipboardAction) { + this.clipboardAction.destroy(); + this.clipboardAction = null; + } + } + }], [{ + key: 'isSupported', + value: function isSupported() { + var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut']; + + var actions = typeof action === 'string' ? [action] : action; + var support = !!document.queryCommandSupported; + + actions.forEach(function (action) { + support = support && !!document.queryCommandSupported(action); + }); + + return support; + } + }]); + + return Clipboard; + }(_tinyEmitter2.default); + + /** + * Helper function to retrieve attribute value. + * @param {String} suffix + * @param {Element} element + */ + function getAttributeValue(suffix, element) { + var attribute = 'data-clipboard-' + suffix; + + if (!element.hasAttribute(attribute)) { + return; + } + + return element.getAttribute(attribute); + } + + module.exports = Clipboard; + }); + + }, {"./clipboard-action": 7, "good-listener": 4, "tiny-emitter": 6}] + }, {}, [8])(8) +}); diff --git a/js/ebanx/clipboard/clipboard.min.js b/js/ebanx/clipboard/clipboard.min.js new file mode 100644 index 00000000..1993676f --- /dev/null +++ b/js/ebanx/clipboard/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v1.6.1 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.Clipboard=e()}}(function(){var e,t,n;return function e(t,n,o){function i(a,c){if(!n[a]){if(!t[a]){var l="function"==typeof require&&require;if(!c&&l)return l(a,!0);if(r)return r(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var s=n[a]={exports:{}};t[a][0].call(s.exports,function(e){var n=t[a][1][e];return i(n?n:e)},s,s.exports,e,t,n,o)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;a0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function e(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function e(){var t=this,n="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=document.body.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[n?"right":"left"]="-9999px";var o=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=o+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,document.body.appendChild(this.fakeElem),this.selectedText=(0,i.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function e(){this.fakeHandler&&(document.body.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(document.body.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function e(){this.selectedText=(0,i.default)(this.target),this.copyText()}},{key:"copyText",value:function e(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function e(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function e(){this.target&&this.target.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function e(){this.removeFake()}},{key:"action",set:function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function e(){return this._action}},{key:"target",set:function e(t){if(void 0!==t){if(!t||"object"!==("undefined"==typeof t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function e(){return this._target}}]),e}();e.exports=c})},{select:5}],8:[function(t,n,o){!function(i,r){if("function"==typeof e&&e.amd)e(["module","./clipboard-action","tiny-emitter","good-listener"],r);else if("undefined"!=typeof o)r(n,t("./clipboard-action"),t("tiny-emitter"),t("good-listener"));else{var a={exports:{}};r(a,i.clipboardAction,i.tinyEmitter,i.goodListener),i.clipboard=a.exports}}(this,function(e,t,n,o){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function c(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e,t){var n="data-clipboard-"+e;if(t.hasAttribute(n))return t.getAttribute(n)}var u=i(t),s=i(n),f=i(o),d=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText}},{key:"listenClick",value:function e(t){var n=this;this.listener=(0,f.default)(t,"click",function(e){return n.onClick(e)})}},{key:"onClick",value:function e(t){var n=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new u.default({action:this.action(n),target:this.target(n),text:this.text(n),trigger:n,emitter:this})}},{key:"defaultAction",value:function e(t){return l("action",t)}},{key:"defaultTarget",value:function e(t){var n=l("target",t);if(n)return document.querySelector(n)}},{key:"defaultText",value:function e(t){return l("text",t)}},{key:"destroy",value:function e(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],n="string"==typeof t?[t]:t,o=!!document.queryCommandSupported;return n.forEach(function(e){o=o&&!!document.queryCommandSupported(e)}),o}}]),t}(s.default);e.exports=h})},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)}); \ No newline at end of file diff --git a/js/ebanx/payment.beautified.js b/js/ebanx/payment.beautified.js new file mode 100644 index 00000000..839da17e --- /dev/null +++ b/js/ebanx/payment.beautified.js @@ -0,0 +1,361 @@ +var EBANXCreditCard = function(a) { + function b(d) { + if (c[d]) return c[d].exports; + var e = c[d] = { + exports: {}, + id: d, + loaded: !1 + }; + return a[d].call(e.exports, e, e.exports, b), e.loaded = !0, e.exports; + } + var c = {}; + return b.m = a, b.c = c, b.p = "", b(0); +}([ function(a, b, c) { + (function(b) { + var d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B = [].indexOf || function(a) { + for (var b = 0, c = this.length; c > b; b++) if (b in this && this[b] === a) return b; + return -1; + }; + e = c(1), i = /(\d{1,4})/g, h = [ { + type: "amex", + pattern: /^3[47]/, + format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/, + length: [ 15 ], + cvcLength: [ 4 ], + luhn: !0 + }, { + type: "dankort", + pattern: /^5019/, + format: i, + length: [ 16 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "dinersclub", + pattern: /^(36|38|30[0-5])/, + format: /(\d{1,4})(\d{1,6})?(\d{1,4})?/, + length: [ 14 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "discover", + pattern: /^(6011|65|64[4-9]|622)/, + format: i, + length: [ 16 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "jcb", + pattern: /^35/, + format: i, + length: [ 16 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "laser", + pattern: /^(6706|6771|6709)/, + format: i, + length: [ 16, 17, 18, 19 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "maestro", + pattern: /^(5018|5020|5038|6304|6703|6708|6759|676[1-3])/, + format: i, + length: [ 12, 13, 14, 15, 16, 17, 18, 19 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "mastercard", + pattern: /^(5[1-5]|677189)|^(222[1-9]|2[3-6]\d{2}|27[0-1]\d|2720)/, + format: i, + length: [ 16 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "unionpay", + pattern: /^62/, + format: i, + length: [ 16, 17, 18, 19 ], + cvcLength: [ 3 ], + luhn: !1 + }, { + type: "visaelectron", + pattern: /^4(026|17500|405|508|844|91[37])/, + format: i, + length: [ 16 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "elo", + pattern: /^(4011|438935|45(1416|76|7393)|50(4175|6699|67|90[4-7])|63(6297|6368))/, + format: i, + length: [ 16 ], + cvcLength: [ 3 ], + luhn: !0 + }, { + type: "visa", + pattern: /^4/, + format: i, + length: [ 13, 16, 19 ], + cvcLength: [ 3 ], + luhn: !0 + } ], f = function(a) { + var b, c, d; + for (a = (a + "").replace(/\D/g, ""), c = 0, d = h.length; d > c; c++) if (b = h[c], + b.pattern.test(a)) return b; + }, g = function(a) { + var b, c, d; + for (c = 0, d = h.length; d > c; c++) if (b = h[c], b.type === a) return b; + }, r = function(a) { + var b, c, d, e, f, g; + for (f = !0, g = 0, c = (a + "").split("").reverse(), d = 0, e = c.length; e > d; d++) b = c[d], + b = parseInt(b, 10), (f = !f) && (b *= 2), b > 9 && (b -= 9), g += b; + return g % 10 === 0; + }, q = function(a) { + var b, c, d; + try { + if (null != a.selectionStart && a.selectionStart !== a.selectionEnd) return !0; + if (null != ("undefined" != typeof document && null !== document && null != (d = document.selection) ? d.createRange : void 0) && document.selection.createRange().text) return !0; + } catch (c) { + b = c; + } + return !1; + }, s = function(a) { + return setTimeout(function() { + return function() { + var b, c; + return b = a.target, c = e.val(b), c = d.fns.formatCardNumber(c), e.val(b, c), e.trigger(b, "change"); + }; + }(this)); + }, l = function(a) { + var b, c, d, g, h, i, j, k, l, m, n; + if (c = String.fromCharCode(a.which), /^\d+$/.test(c)) { + for (k = a.target, n = e.val(k), b = f(n + c), i = (n.replace(/\D/g, "") + c).length, + m = [ 16 ], b && (m = b.length), d = g = 0, h = m.length; h > g; d = ++g) if (l = m[d], + !(i >= l && m[d + 1]) && i >= l) return; + if (!q(k)) return j = b && "amex" === b.type ? /^(\d{4}|\d{4}\s\d{6})$/ : /(?:^|\s)(\d{4})$/, + j.test(n) ? (a.preventDefault(), e.val(k, n + " " + c), e.trigger(k, "change")) : void 0; + } + }, j = function(a) { + var b, c; + return b = a.target, c = e.val(b), a.meta || 8 !== a.which || q(b) ? void 0 : /\d\s$/.test(c) ? (a.preventDefault(), + e.val(b, c.replace(/\d\s$/, "")), e.trigger(b, "change")) : /\s\d?$/.test(c) ? (a.preventDefault(), + e.val(b, c.replace(/\s\d?$/, "")), e.trigger(b, "change")) : void 0; + }, m = function(a) { + var b, c, d; + return b = String.fromCharCode(a.which), /^\d+$/.test(b) ? (c = a.target, d = e.val(c) + b, + /^\d$/.test(d) && "0" !== d && "1" !== d ? (a.preventDefault(), e.val(c, "0" + d + " / "), + e.trigger(c, "change")) : /^\d\d$/.test(d) ? (a.preventDefault(), e.val(c, d + " / "), + e.trigger(c, "change")) : void 0) : void 0; + }, p = function(a) { + var b, c, d; + return b = String.fromCharCode(a.which), /^\d+$/.test(b) ? (c = a.target, d = e.val(c) + b, + /^\d$/.test(d) && "0" !== d && "1" !== d ? (a.preventDefault(), e.val(c, "0" + d), + e.trigger(c, "change")) : /^\d\d$/.test(d) ? (a.preventDefault(), e.val(c, "" + d), + e.trigger(c, "change")) : void 0) : void 0; + }, n = function(a) { + var b, c, d; + return b = String.fromCharCode(a.which), /^\d+$/.test(b) ? (c = a.target, d = e.val(c), + /^\d\d$/.test(d) ? (e.val(c, d + " / "), e.trigger(c, "change")) : void 0) : void 0; + }, o = function(a) { + var b, c, d; + return b = String.fromCharCode(a.which), "/" === b ? (c = a.target, d = e.val(c), + /^\d$/.test(d) && "0" !== d ? (e.val(c, "0" + d + " / "), e.trigger(c, "change")) : void 0) : void 0; + }, k = function(a) { + var b, c; + if (!a.metaKey && (b = a.target, c = e.val(b), 8 === a.which && !q(b))) return /\d(\s|\/)+$/.test(c) ? (a.preventDefault(), + e.val(b, c.replace(/\d(\s|\/)*$/, "")), e.trigger(b, "change")) : /\s\/\s?\d?$/.test(c) ? (a.preventDefault(), + e.val(b, c.replace(/\s\/\s?\d?$/, "")), e.trigger(b, "change")) : void 0; + }, y = function(a) { + var b; + return a.metaKey || a.ctrlKey ? !0 : 32 === a.which ? a.preventDefault() : 0 === a.which ? !0 : a.which < 33 ? !0 : (b = String.fromCharCode(a.which), + /[\d\s]/.test(b) ? void 0 : a.preventDefault()); + }, u = function(a) { + var b, c, d, g; + if (d = a.target, c = String.fromCharCode(a.which), /^\d+$/.test(c) && !q(d)) if (g = (e.val(d) + c).replace(/\D/g, ""), + b = f(g)) { + if (!(g.length <= b.length[b.length.length - 1])) return a.preventDefault(); + } else if (!(g.length <= 16)) return a.preventDefault(); + }, w = function(a, b) { + var c, d, f; + return d = a.target, c = String.fromCharCode(a.which), /^\d+$/.test(c) && !q(d) ? (f = e.val(d) + c, + f = f.replace(/\D/g, ""), f.length > b ? a.preventDefault() : void 0) : void 0; + }, v = function(a) { + return w(a, 6); + }, x = function(a) { + return w(a, 2); + }, z = function(a) { + return w(a, 4); + }, t = function(a) { + var b, c, d; + return c = a.target, b = String.fromCharCode(a.which), /^\d+$/.test(b) && !q(c) ? (d = e.val(c) + b, + d.length <= 4 ? void 0 : a.preventDefault()) : void 0; + }, A = function(a) { + var b, c, f, g, i; + return g = a.target, i = e.val(g), f = d.fns.cardType(i) || "unknown", e.hasClass(g, f) ? void 0 : (b = function() { + var a, b, d; + for (d = [], a = 0, b = h.length; b > a; a++) c = h[a], d.push(c.type); + return d; + }(), e.removeClass(g, "unknown"), e.removeClass(g, b.join(" ")), e.addClass(g, f), + e.toggleClass(g, "identified", "unknown" !== f), e.trigger(g, "payment.cardType", f)); + }, d = function() { + function a() {} + return a.fns = { + cardExpiryVal: function(a) { + var b, c, d, e; + return a = a.replace(/\s/g, ""), d = a.split("/", 2), b = d[0], e = d[1], 2 === (null != e ? e.length : void 0) && /^\d+$/.test(e) && (c = new Date().getFullYear(), + c = c.toString().slice(0, 2), e = c + e), b = parseInt(b, 10), e = parseInt(e, 10), + { + month: b, + year: e + }; + }, + validateCardNumber: function(a) { + var b, c; + return a = (a + "").replace(/\s+|-/g, ""), /^\d+$/.test(a) ? (b = f(a), b ? (c = a.length, + B.call(b.length, c) >= 0 && (b.luhn === !1 || r(a))) : !1) : !1; + }, + validateCardExpiry: function(b, c) { + var d, f, g, h, i; + return "object" == typeof b && "month" in b ? (h = b, b = h.month, c = h.year) : "string" == typeof b && B.call(b, "/") >= 0 && (i = a.fns.cardExpiryVal(b), + b = i.month, c = i.year), b && c ? (b = e.trim(b), c = e.trim(c), /^\d+$/.test(b) && /^\d+$/.test(c) ? (b = parseInt(b, 10), + b && 12 >= b ? (2 === c.length && (g = new Date().getFullYear(), g = g.toString().slice(0, 2), + c = g + c), f = new Date(c, b), d = new Date(), f.setMonth(f.getMonth() - 1), f.setMonth(f.getMonth() + 1, 1), + f > d) : !1) : !1) : !1; + }, + validateCardCVC: function(a, b) { + var c, d; + return a = e.trim(a), /^\d+$/.test(a) ? b && g(b) ? (c = a.length, B.call(null != (d = g(b)) ? d.cvcLength : void 0, c) >= 0) : a.length >= 3 && a.length <= 4 : !1; + }, + cardType: function(a) { + var b; + return a ? (null != (b = f(a)) ? b.type : void 0) || null : null; + }, + formatCardNumber: function(a) { + var b, c, d, e; + return (b = f(a)) ? (e = b.length[b.length.length - 1], a = a.replace(/\D/g, ""), + a = a.slice(0, e), b.format.global ? null != (d = a.match(b.format)) ? d.join(" ") : void 0 : (c = b.format.exec(a), + null != c && c.shift(), null != c ? c.join(" ") : void 0)) : a; + } + }, a.restrictNumeric = function(a) { + return e.on(a, "keypress", y); + }, a.cardExpiryVal = function(b) { + return a.fns.cardExpiryVal(e.val(b)); + }, a.formatCardCVC = function(b) { + return a.restrictNumeric(b), e.on(b, "keypress", t), b; + }, a.formatCardExpiry = function(b) { + var c, d; + return a.restrictNumeric(b), b.length && 2 === b.length ? (c = b[0], d = b[1], this.formatCardExpiryMultiple(c, d)) : (e.on(b, "keypress", v), + e.on(b, "keypress", m), e.on(b, "keypress", o), e.on(b, "keypress", n), e.on(b, "keydown", k)), + b; + }, a.formatCardExpiryMultiple = function(a, b) { + return e.on(a, "keypress", x), e.on(a, "keypress", p), e.on(b, "keypress", z); + }, a.formatCardNumber = function(b) { + return a.restrictNumeric(b), e.on(b, "keypress", u), e.on(b, "keypress", l), e.on(b, "keydown", j), + e.on(b, "keyup blur", A), e.on(b, "paste", s), b; + }, a.getCardArray = function() { + return h; + }, a.setCardArray = function(a) { + return h = a, !0; + }, a.addToCardArray = function(a) { + return h.push(a); + }, a.removeFromCardArray = function(a) { + var b, c; + for (b in h) c = h[b], c.type === a && h.splice(b, 1); + return !0; + }, a; + }(), a.exports = d, b.Payment = d; + }).call(b, function() { + return this; + }()); +}, function(a) { + (function() { + var b, c, d; + b = function(a) { + return b.isDOMElement(a) ? a : document.querySelectorAll(a); + }, b.isDOMElement = function(a) { + return a && null != a.nodeName; + }, d = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, b.trim = function(a) { + return null === a ? "" : (a + "").replace(d, ""); + }, c = /\r/g, b.val = function(a, b) { + var d; + return arguments.length > 1 ? a.value = b : (d = a.value, "string" == typeof d ? d.replace(c, "") : null === d ? "" : d); + }, b.preventDefault = function(a) { + return "function" == typeof a.preventDefault ? void a.preventDefault() : (a.returnValue = !1, + !1); + }, b.normalizeEvent = function(a) { + var c; + return c = a, a = { + which: null != c.which ? c.which : void 0, + target: c.target || c.srcElement, + preventDefault: function() { + return b.preventDefault(c); + }, + originalEvent: c, + data: c.data || c.detail + }, null == a.which && (a.which = null != c.charCode ? c.charCode : c.keyCode), a; + }, b.on = function(a, c, d) { + var e, f, g, h, i, j, k, l; + if (a.length) for (f = 0, h = a.length; h > f; f++) e = a[f], b.on(e, c, d); else { + if (!c.match(" ")) return k = d, d = function(a) { + return a = b.normalizeEvent(a), k(a); + }, a.addEventListener ? a.addEventListener(c, d, !1) : a.attachEvent ? (c = "on" + c, + a.attachEvent(c, d)) : void (a["on" + c] = d); + for (l = c.split(" "), g = 0, i = l.length; i > g; g++) j = l[g], b.on(a, j, d); + } + }, b.addClass = function(a, c) { + var d; + return a.length ? function() { + var e, f, g; + for (g = [], e = 0, f = a.length; f > e; e++) d = a[e], g.push(b.addClass(d, c)); + return g; + }() : a.classList ? a.classList.add(c) : a.className += " " + c; + }, b.hasClass = function(a, c) { + var d, e, f, g; + if (a.length) { + for (e = !0, f = 0, g = a.length; g > f; f++) d = a[f], e = e && b.hasClass(d, c); + return e; + } + return a.classList ? a.classList.contains(c) : new RegExp("(^| )" + c + "( |$)", "gi").test(a.className); + }, b.removeClass = function(a, c) { + var d, e, f, g, h, i; + if (a.length) return function() { + var d, f, g; + for (g = [], d = 0, f = a.length; f > d; d++) e = a[d], g.push(b.removeClass(e, c)); + return g; + }(); + if (a.classList) { + for (h = c.split(" "), i = [], f = 0, g = h.length; g > f; f++) d = h[f], i.push(a.classList.remove(d)); + return i; + } + return a.className = a.className.replace(new RegExp("(^|\\b)" + c.split(" ").join("|") + "(\\b|$)", "gi"), " "); + }, b.toggleClass = function(a, c, d) { + var e; + return a.length ? function() { + var f, g, h; + for (h = [], f = 0, g = a.length; g > f; f++) e = a[f], h.push(b.toggleClass(e, c, d)); + return h; + }() : d ? b.hasClass(a, c) ? void 0 : b.addClass(a, c) : b.removeClass(a, c); + }, b.append = function(a, c) { + var d; + return a.length ? function() { + var e, f, g; + for (g = [], e = 0, f = a.length; f > e; e++) d = a[e], g.push(b.append(d, c)); + return g; + }() : a.insertAdjacentHTML("beforeend", c); + }, b.find = function(a, b) { + return (a instanceof NodeList || a instanceof Array) && (a = a[0]), a.querySelectorAll(b); + }, b.trigger = function(a, b, c) { + var d, e, f; + try { + f = new CustomEvent(b, { + detail: c + }); + } catch (e) { + d = e, f = document.createEvent("CustomEvent"), f.initCustomEvent ? f.initCustomEvent(b, !0, !0, c) : f.initEvent(b, !0, !0, c); + } + return a.dispatchEvent(f); + }, a.exports = b; + }).call(this); +} ]); \ No newline at end of file diff --git a/js/ebanx/payment.js b/js/ebanx/payment.js new file mode 100644 index 00000000..b50f1b5c --- /dev/null +++ b/js/ebanx/payment.js @@ -0,0 +1 @@ +var EBANXCreditCard=function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){(function(b){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};e=c(1),i=/(\d{1,4})/g,h=[{type:"amex",pattern:/^3[47]/,format:/(\d{1,4})(\d{1,6})?(\d{1,5})?/,length:[15],cvcLength:[4],luhn:!0},{type:"dankort",pattern:/^5019/,format:i,length:[16],cvcLength:[3],luhn:!0},{type:"dinersclub",pattern:/^(36|38|30[0-5])/,format:/(\d{1,4})(\d{1,6})?(\d{1,4})?/,length:[14],cvcLength:[3],luhn:!0},{type:"discover",pattern:/^(6011|65|64[4-9]|622)/,format:i,length:[16],cvcLength:[3],luhn:!0},{type:"jcb",pattern:/^35/,format:i,length:[16],cvcLength:[3],luhn:!0},{type:"laser",pattern:/^(6706|6771|6709)/,format:i,length:[16],cvcLength:[3],luhn:!0},{type:"maestro",pattern:/^(5018|5020|5038|6304|6703|6708|6759|676[1-3])/,format:i,length:[12,13,14,15,16],cvcLength:[3],luhn:!0},{type:"mastercard",pattern:/^(5[1-5]|677189)|^(222[1-9]|2[3-6]\d{2}|27[0-1]\d|2720)/,format:i,length:[16],cvcLength:[3],luhn:!0},{type:"unionpay",pattern:/^62/,format:i,length:[16],cvcLength:[3],luhn:!1},{type:"visaelectron",pattern:/^4(026|17500|405|508|844|91[37])/,format:i,length:[16],cvcLength:[3],luhn:!0},{type:"elo",pattern:/^(4011|438935|45(1416|76|7393)|50(4175|6699|67|90[4-7])|63(6297|6368))/,format:i,length:[16],cvcLength:[3],luhn:!0},{type:"visa",pattern:/^4/,format:i,length:[13,16],cvcLength:[3],luhn:!0}],f=function(a){var b,c,d;for(a=(a+"").replace(/\D/g,""),c=0,d=h.length;d>c;c++)if(b=h[c],b.pattern.test(a))return b},g=function(a){var b,c,d;for(c=0,d=h.length;d>c;c++)if(b=h[c],b.type===a)return b},r=function(a){var b,c,d,e,f,g;for(f=!0,g=0,c=(a+"").split("").reverse(),d=0,e=c.length;e>d;d++)b=c[d],b=parseInt(b,10),(f=!f)&&(b*=2),b>9&&(b-=9),g+=b;return g%10===0},q=function(a){var b,c,d;try{if(null!=a.selectionStart&&a.selectionStart!==a.selectionEnd)return!0;if(null!=("undefined"!=typeof document&&null!==document&&null!=(d=document.selection)?d.createRange:void 0)&&document.selection.createRange().text)return!0}catch(c){b=c}return!1},s=function(a){return setTimeout(function(){return function(){var b,c;return b=a.target,c=e.val(b),c=d.fns.formatCardNumber(c),e.val(b,c),e.trigger(b,"change")}}(this))},l=function(a){var b,c,d,g,h,i,j,k,l,m,n;if(c=String.fromCharCode(a.which),/^\d+$/.test(c)){for(k=a.target,n=e.val(k),b=f(n+c),i=(n.replace(/\D/g,"")+c).length,m=[16],b&&(m=b.length),d=g=0,h=m.length;h>g;d=++g)if(l=m[d],!(i>=l&&m[d+1])&&i>=l)return;if(!q(k))return j=b&&"amex"===b.type?/^(\d{4}|\d{4}\s\d{6})$/:/(?:^|\s)(\d{4})$/,j.test(n)?(a.preventDefault(),e.val(k,n+" "+c),e.trigger(k,"change")):void 0}},j=function(a){var b,c;return b=a.target,c=e.val(b),a.meta||8!==a.which||q(b)?void 0:/\d\s$/.test(c)?(a.preventDefault(),e.val(b,c.replace(/\d\s$/,"")),e.trigger(b,"change")):/\s\d?$/.test(c)?(a.preventDefault(),e.val(b,c.replace(/\s\d?$/,"")),e.trigger(b,"change")):void 0},m=function(a){var b,c,d;return b=String.fromCharCode(a.which),/^\d+$/.test(b)?(c=a.target,d=e.val(c)+b,/^\d$/.test(d)&&"0"!==d&&"1"!==d?(a.preventDefault(),e.val(c,"0"+d+" / "),e.trigger(c,"change")):/^\d\d$/.test(d)?(a.preventDefault(),e.val(c,d+" / "),e.trigger(c,"change")):void 0):void 0},p=function(a){var b,c,d;return b=String.fromCharCode(a.which),/^\d+$/.test(b)?(c=a.target,d=e.val(c)+b,/^\d$/.test(d)&&"0"!==d&&"1"!==d?(a.preventDefault(),e.val(c,"0"+d),e.trigger(c,"change")):/^\d\d$/.test(d)?(a.preventDefault(),e.val(c,""+d),e.trigger(c,"change")):void 0):void 0},n=function(a){var b,c,d;return b=String.fromCharCode(a.which),/^\d+$/.test(b)?(c=a.target,d=e.val(c),/^\d\d$/.test(d)?(e.val(c,d+" / "),e.trigger(c,"change")):void 0):void 0},o=function(a){var b,c,d;return b=String.fromCharCode(a.which),"/"===b?(c=a.target,d=e.val(c),/^\d$/.test(d)&&"0"!==d?(e.val(c,"0"+d+" / "),e.trigger(c,"change")):void 0):void 0},k=function(a){var b,c;if(!a.metaKey&&(b=a.target,c=e.val(b),8===a.which&&!q(b)))return/\d(\s|\/)+$/.test(c)?(a.preventDefault(),e.val(b,c.replace(/\d(\s|\/)*$/,"")),e.trigger(b,"change")):/\s\/\s?\d?$/.test(c)?(a.preventDefault(),e.val(b,c.replace(/\s\/\s?\d?$/,"")),e.trigger(b,"change")):void 0},y=function(a){var b;return a.metaKey||a.ctrlKey?!0:32===a.which?a.preventDefault():0===a.which?!0:a.which<33?!0:(b=String.fromCharCode(a.which),/[\d\s]/.test(b)?void 0:a.preventDefault())},u=function(a){var b,c,d,g;if(d=a.target,c=String.fromCharCode(a.which),/^\d+$/.test(c)&&!q(d))if(g=(e.val(d)+c).replace(/\D/g,""),b=f(g)){if(!(g.length<=b.length[b.length.length-1]))return a.preventDefault()}else if(!(g.length<=16))return a.preventDefault()},w=function(a,b){var c,d,f;return d=a.target,c=String.fromCharCode(a.which),/^\d+$/.test(c)&&!q(d)?(f=e.val(d)+c,f=f.replace(/\D/g,""),f.length>b?a.preventDefault():void 0):void 0},v=function(a){return w(a,6)},x=function(a){return w(a,2)},z=function(a){return w(a,4)},t=function(a){var b,c,d;return c=a.target,b=String.fromCharCode(a.which),/^\d+$/.test(b)&&!q(c)?(d=e.val(c)+b,d.length<=4?void 0:a.preventDefault()):void 0},A=function(a){var b,c,f,g,i;return g=a.target,i=e.val(g),f=d.fns.cardType(i)||"unknown",e.hasClass(g,f)?void 0:(b=function(){var a,b,d;for(d=[],a=0,b=h.length;b>a;a++)c=h[a],d.push(c.type);return d}(),e.removeClass(g,"unknown"),e.removeClass(g,b.join(" ")),e.addClass(g,f),e.toggleClass(g,"identified","unknown"!==f),e.trigger(g,"payment.cardType",f))},d=function(){function a(){}return a.fns={cardExpiryVal:function(a){var b,c,d,e;return a=a.replace(/\s/g,""),d=a.split("/",2),b=d[0],e=d[1],2===(null!=e?e.length:void 0)&&/^\d+$/.test(e)&&(c=(new Date).getFullYear(),c=c.toString().slice(0,2),e=c+e),b=parseInt(b,10),e=parseInt(e,10),{month:b,year:e}},validateCardNumber:function(a){var b,c;return a=(a+"").replace(/\s+|-/g,""),/^\d+$/.test(a)?(b=f(a),b?(c=a.length,B.call(b.length,c)>=0&&(b.luhn===!1||r(a))):!1):!1},validateCardExpiry:function(b,c){var d,f,g,h,i;return"object"==typeof b&&"month"in b?(h=b,b=h.month,c=h.year):"string"==typeof b&&B.call(b,"/")>=0&&(i=a.fns.cardExpiryVal(b),b=i.month,c=i.year),b&&c?(b=e.trim(b),c=e.trim(c),/^\d+$/.test(b)&&/^\d+$/.test(c)?(b=parseInt(b,10),b&&12>=b?(2===c.length&&(g=(new Date).getFullYear(),g=g.toString().slice(0,2),c=g+c),f=new Date(c,b),d=new Date,f.setMonth(f.getMonth()-1),f.setMonth(f.getMonth()+1,1),f>d):!1):!1):!1},validateCardCVC:function(a,b){var c,d;return a=e.trim(a),/^\d+$/.test(a)?b&&g(b)?(c=a.length,B.call(null!=(d=g(b))?d.cvcLength:void 0,c)>=0):a.length>=3&&a.length<=4:!1},cardType:function(a){var b;return a?(null!=(b=f(a))?b.type:void 0)||null:null},formatCardNumber:function(a){var b,c,d,e;return(b=f(a))?(e=b.length[b.length.length-1],a=a.replace(/\D/g,""),a=a.slice(0,e),b.format.global?null!=(d=a.match(b.format))?d.join(" "):void 0:(c=b.format.exec(a),null!=c&&c.shift(),null!=c?c.join(" "):void 0)):a}},a.restrictNumeric=function(a){return e.on(a,"keypress",y)},a.cardExpiryVal=function(b){return a.fns.cardExpiryVal(e.val(b))},a.formatCardCVC=function(b){return a.restrictNumeric(b),e.on(b,"keypress",t),b},a.formatCardExpiry=function(b){var c,d;return a.restrictNumeric(b),b.length&&2===b.length?(c=b[0],d=b[1],this.formatCardExpiryMultiple(c,d)):(e.on(b,"keypress",v),e.on(b,"keypress",m),e.on(b,"keypress",o),e.on(b,"keypress",n),e.on(b,"keydown",k)),b},a.formatCardExpiryMultiple=function(a,b){return e.on(a,"keypress",x),e.on(a,"keypress",p),e.on(b,"keypress",z)},a.formatCardNumber=function(b){return a.restrictNumeric(b),e.on(b,"keypress",u),e.on(b,"keypress",l),e.on(b,"keydown",j),e.on(b,"keyup blur",A),e.on(b,"paste",s),b},a.getCardArray=function(){return h},a.setCardArray=function(a){return h=a,!0},a.addToCardArray=function(a){return h.push(a)},a.removeFromCardArray=function(a){var b,c;for(b in h)c=h[b],c.type===a&&h.splice(b,1);return!0},a}(),a.exports=d,b.Payment=d}).call(b,function(){return this}())},function(a){(function(){var b,c,d;b=function(a){return b.isDOMElement(a)?a:document.querySelectorAll(a)},b.isDOMElement=function(a){return a&&null!=a.nodeName},d=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,b.trim=function(a){return null===a?"":(a+"").replace(d,"")},c=/\r/g,b.val=function(a,b){var d;return arguments.length>1?a.value=b:(d=a.value,"string"==typeof d?d.replace(c,""):null===d?"":d)},b.preventDefault=function(a){return"function"==typeof a.preventDefault?void a.preventDefault():(a.returnValue=!1,!1)},b.normalizeEvent=function(a){var c;return c=a,a={which:null!=c.which?c.which:void 0,target:c.target||c.srcElement,preventDefault:function(){return b.preventDefault(c)},originalEvent:c,data:c.data||c.detail},null==a.which&&(a.which=null!=c.charCode?c.charCode:c.keyCode),a},b.on=function(a,c,d){var e,f,g,h,i,j,k,l;if(a.length)for(f=0,h=a.length;h>f;f++)e=a[f],b.on(e,c,d);else{if(!c.match(" "))return k=d,d=function(a){return a=b.normalizeEvent(a),k(a)},a.addEventListener?a.addEventListener(c,d,!1):a.attachEvent?(c="on"+c,a.attachEvent(c,d)):void(a["on"+c]=d);for(l=c.split(" "),g=0,i=l.length;i>g;g++)j=l[g],b.on(a,j,d)}},b.addClass=function(a,c){var d;return a.length?function(){var e,f,g;for(g=[],e=0,f=a.length;f>e;e++)d=a[e],g.push(b.addClass(d,c));return g}():a.classList?a.classList.add(c):a.className+=" "+c},b.hasClass=function(a,c){var d,e,f,g;if(a.length){for(e=!0,f=0,g=a.length;g>f;f++)d=a[f],e=e&&b.hasClass(d,c);return e}return a.classList?a.classList.contains(c):new RegExp("(^| )"+c+"( |$)","gi").test(a.className)},b.removeClass=function(a,c){var d,e,f,g,h,i;if(a.length)return function(){var d,f,g;for(g=[],d=0,f=a.length;f>d;d++)e=a[d],g.push(b.removeClass(e,c));return g}();if(a.classList){for(h=c.split(" "),i=[],f=0,g=h.length;g>f;f++)d=h[f],i.push(a.classList.remove(d));return i}return a.className=a.className.replace(new RegExp("(^|\\b)"+c.split(" ").join("|")+"(\\b|$)","gi")," ")},b.toggleClass=function(a,c,d){var e;return a.length?function(){var f,g,h;for(h=[],f=0,g=a.length;g>f;f++)e=a[f],h.push(b.toggleClass(e,c,d));return h}():d?b.hasClass(a,c)?void 0:b.addClass(a,c):b.removeClass(a,c)},b.append=function(a,c){var d;return a.length?function(){var e,f,g;for(g=[],e=0,f=a.length;f>e;e++)d=a[e],g.push(b.append(d,c));return g}():a.insertAdjacentHTML("beforeend",c)},b.find=function(a,b){return(a instanceof NodeList||a instanceof Array)&&(a=a[0]),a.querySelectorAll(b)},b.trigger=function(a,b,c){var d,e,f;try{f=new CustomEvent(b,{detail:c})}catch(e){d=e,f=document.createEvent("CustomEvent"),f.initCustomEvent?f.initCustomEvent(b,!0,!0,c):f.initEvent(b,!0,!0,c)}return a.dispatchEvent(f)},a.exports=b}).call(this)}]); diff --git a/js/ebanx/vanilla-masker/vanilla-masker.min.js b/js/ebanx/vanilla-masker/vanilla-masker.min.js new file mode 100644 index 00000000..729fd574 --- /dev/null +++ b/js/ebanx/vanilla-masker/vanilla-masker.min.js @@ -0,0 +1 @@ +!function(a,b){"function"==typeof define&&define.amd?define(b):a.VMasker=b()}(this,function(){var a="9",b="A",c="S",d=[8,9,16,17,18,36,37,38,39,40,91,92,93],e=function(a){for(var b=0,c=d.length;c>b;b++)if(a==d[b])return!1;return!0},f=function(a){return a=a||{},a={precision:a.hasOwnProperty("precision")?a.precision:2,separator:a.separator||",",delimiter:a.delimiter||".",unit:a.unit&&a.unit.replace(/[\s]/g,"")+" "||"",suffixUnit:a.suffixUnit&&" "+a.suffixUnit.replace(/[\s]/g,"")||"",zeroCents:a.zeroCents,lastOutput:a.lastOutput},a.moneyPrecision=a.zeroCents?0:a.precision,a},g=function(a){this.elements=a};g.prototype.unbindElementToMask=function(){for(var a=0,b=this.elements.length;b>a;a++)this.elements[a].lastOutput="",this.elements[a].onkeyup=!1,this.elements[a].onkeydown=!1,this.elements[a].value.length&&(this.elements[a].value=this.elements[a].value.replace(/\D/g,""))},g.prototype.bindElementToMask=function(a){for(var b=this,c=function(c){c=c||window.event;var d=c.target||c.srcElement;e(c.keyCode)&&setTimeout(function(){b.opts.lastOutput=d.lastOutput,d.value=h[a](d.value,b.opts),d.lastOutput=d.value,d.setSelectionRange&&b.opts.suffixUnit&&d.setSelectionRange(d.value.length,d.value.length-b.opts.suffixUnit.length)},0)},d=0,f=this.elements.length;f>d;d++)this.elements[d].lastOutput="",this.elements[d].onkeyup=c,this.elements[d].value.length&&(this.elements[d].value=h[a](this.elements[d].value,this.opts))},g.prototype.maskMoney=function(a){this.opts=f(a),this.bindElementToMask("toMoney")},g.prototype.maskNumber=function(){this.opts={},this.bindElementToMask("toNumber")},g.prototype.maskPattern=function(a){this.opts={pattern:a},this.bindElementToMask("toPattern")},g.prototype.unMask=function(){this.unbindElementToMask()};var h=function(a){if(!a)throw new Error("VanillaMasker: There is no element to bind.");var b="length"in a?a.length?a:[]:[a];return new g(b)};return h.toMoney=function(a,b){if(b=f(b),b.zeroCents){b.lastOutput=b.lastOutput||"";var c="("+b.separator+"[0]{0,"+b.precision+"})",d=new RegExp(c,"g"),e=a.toString().replace(/[\D]/g,"").length||0,g=b.lastOutput.toString().replace(/[\D]/g,"").length||0;a=a.toString().replace(d,""),g>e&&(a=a.slice(0,a.length-1))}var h=a.toString().replace(/[\D]/g,""),i=new RegExp("^(0|\\"+b.delimiter+")"),j=new RegExp("(\\"+b.separator+")$"),k=h.substr(0,h.length-b.moneyPrecision),l=k.substr(0,k.length%3),m=new Array(b.precision+1).join("0");k=k.substr(k.length%3,k.length);for(var n=0,o=k.length;o>n;n++)n%3===0&&(l+=b.delimiter),l+=k[n];if(l=l.replace(i,""),l=l.length?l:"0",!b.zeroCents){var p=h.length-b.precision,q=h.substr(p,b.precision),r=q.length,s=b.precision>r?b.precision:r;m=(m+q).slice(-s)}var t=b.unit+l+b.separator+m+b.suffixUnit;return t.replace(j,"")},h.toPattern=function(d,e){var f,g="object"==typeof e?e.pattern:e,h=g.replace(/\W/g,""),i=g.split(""),j=d.toString().replace(/\W/g,""),k=j.replace(/\W/g,""),l=0,m=i.length;for(f=0;m>f;f++){if(l>=j.length){if(h.length==k.length)return i.join("");break}i[f]===a&&j[l].match(/[0-9]/)||i[f]===b&&j[l].match(/[a-zA-Z]/)||i[f]===c&&j[l].match(/[0-9a-zA-Z]/)?i[f]=j[l++]:(i[f]===a||i[f]===b||i[f]===c)&&(i=i.slice(0,f))}return i.join("").substr(0,f)},h.toNumber=function(a){return a.toString().replace(/(?!^-)[^0-9]/g,"")},h}); \ No newline at end of file diff --git a/lib/Ebanx/vendor/autoload.php b/lib/Ebanx/vendor/autoload.php new file mode 100644 index 00000000..eb287a87 --- /dev/null +++ b/lib/Ebanx/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath.'\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/lib/Ebanx/vendor/composer/LICENSE b/lib/Ebanx/vendor/composer/LICENSE new file mode 100644 index 00000000..f27399a0 --- /dev/null +++ b/lib/Ebanx/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/lib/Ebanx/vendor/composer/autoload_classmap.php b/lib/Ebanx/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000..024622f7 --- /dev/null +++ b/lib/Ebanx/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/react/promise/src/functions_include.php', + 'a0063ca44df31a81bb0634cab48f040a' => $vendorDir . '/ebanx/benjamin/main.php', +); diff --git a/lib/Ebanx/vendor/composer/autoload_namespaces.php b/lib/Ebanx/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000..9bd4acee --- /dev/null +++ b/lib/Ebanx/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/react/promise/src'), + 'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'), + 'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'), + 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), + 'Ebanx\\Benjamin\\' => array($vendorDir . '/ebanx/benjamin/src'), +); diff --git a/lib/Ebanx/vendor/composer/autoload_real.php b/lib/Ebanx/vendor/composer/autoload_real.php new file mode 100644 index 00000000..74ff27a4 --- /dev/null +++ b/lib/Ebanx/vendor/composer/autoload_real.php @@ -0,0 +1,70 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticIniteb82de9299d1c4eff1e4e8129a031dfd::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticIniteb82de9299d1c4eff1e4e8129a031dfd::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequireeb82de9299d1c4eff1e4e8129a031dfd($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequireeb82de9299d1c4eff1e4e8129a031dfd($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/lib/Ebanx/vendor/composer/autoload_static.php b/lib/Ebanx/vendor/composer/autoload_static.php new file mode 100644 index 00000000..1fe24efa --- /dev/null +++ b/lib/Ebanx/vendor/composer/autoload_static.php @@ -0,0 +1,62 @@ + __DIR__ . '/..' . '/react/promise/src/functions_include.php', + 'a0063ca44df31a81bb0634cab48f040a' => __DIR__ . '/..' . '/ebanx/benjamin/main.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'R' => + array ( + 'React\\Promise\\' => 14, + ), + 'G' => + array ( + 'GuzzleHttp\\Stream\\' => 18, + 'GuzzleHttp\\Ring\\' => 16, + 'GuzzleHttp\\' => 11, + ), + 'E' => + array ( + 'Ebanx\\Benjamin\\' => 15, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'React\\Promise\\' => + array ( + 0 => __DIR__ . '/..' . '/react/promise/src', + ), + 'GuzzleHttp\\Stream\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/streams/src', + ), + 'GuzzleHttp\\Ring\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/ringphp/src', + ), + 'GuzzleHttp\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', + ), + 'Ebanx\\Benjamin\\' => + array ( + 0 => __DIR__ . '/..' . '/ebanx/benjamin/src', + ), + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticIniteb82de9299d1c4eff1e4e8129a031dfd::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticIniteb82de9299d1c4eff1e4e8129a031dfd::$prefixDirsPsr4; + + }, null, ClassLoader::class); + } +} diff --git a/lib/Ebanx/vendor/composer/installed.json b/lib/Ebanx/vendor/composer/installed.json new file mode 100644 index 00000000..03b8a386 --- /dev/null +++ b/lib/Ebanx/vendor/composer/installed.json @@ -0,0 +1,254 @@ +[ + { + "name": "ebanx/benjamin", + "version": "1.10.0", + "version_normalized": "1.10.0.0", + "source": { + "type": "git", + "url": "https://github.com/ebanx/benjamin.git", + "reference": "f234b8beedc7243b23a92bbd3752e85b6c43aa36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ebanx/benjamin/zipball/f234b8beedc7243b23a92bbd3752e85b6c43aa36", + "reference": "f234b8beedc7243b23a92bbd3752e85b6c43aa36", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^5.3.1", + "php": ">=5.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.2.3", + "fzaninotto/faker": "^1.6@dev", + "justinrainbow/json-schema": "^5.0@dev", + "phpunit/phpunit": "^4.8", + "sebastian/phpcpd": "^2.0.4", + "vlucas/phpdotenv": "^2.4@dev" + }, + "time": "2018-04-13T14:10:47+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Ebanx\\Benjamin\\": "src/" + }, + "files": [ + "main.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Business rule provider lib for plugin implementations" + }, + { + "name": "guzzlehttp/guzzle", + "version": "5.3.x-dev", + "version_normalized": "5.3.9999999.9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f9acb4761844317e626a32259205bec1f1bc60d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f9acb4761844317e626a32259205bec1f1bc60d2", + "reference": "f9acb4761844317e626a32259205bec1f1bc60d2", + "shasum": "" + }, + "require": { + "guzzlehttp/ringphp": "^1.1", + "php": ">=5.4.0", + "react/promise": "^2.2" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0" + }, + "time": "2018-01-15T07:18:01+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "name": "guzzlehttp/ringphp", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/guzzle/RingPHP.git", + "reference": "4c8fe4c48a0fb7cc5e41ef529e43fecd6da4d539" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/4c8fe4c48a0fb7cc5e41ef529e43fecd6da4d539", + "reference": "4c8fe4c48a0fb7cc5e41ef529e43fecd6da4d539", + "shasum": "" + }, + "require": { + "guzzlehttp/streams": "~3.0", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "time": "2017-01-13T20:44:38+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function." + }, + { + "name": "guzzlehttp/streams", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/guzzle/streams.git", + "reference": "d99a261c616210618ab94fd319cb17eda458cc3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/streams/zipball/d99a261c616210618ab94fd319cb17eda458cc3e", + "reference": "d99a261c616210618ab94fd319cb17eda458cc3e", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2016-04-13T16:32:01+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ] + }, + { + "name": "react/promise", + "version": "2.x-dev", + "version_normalized": "2.9999999.9999999.9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "179520ec0b25cf4c742d9653be9f4964d64d4a6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/179520ec0b25cf4c742d9653be9f4964d64d4a6e", + "reference": "179520ec0b25cf4c742d9653be9f4964d64d4a6e", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "time": "2017-09-19T13:02:44+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ] + } +] diff --git a/lib/Ebanx/vendor/ebanx/benjamin/CHANGELOG.md b/lib/Ebanx/vendor/ebanx/benjamin/CHANGELOG.md new file mode 100644 index 00000000..d15b03fd --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/CHANGELOG.md @@ -0,0 +1,66 @@ +## 1.10.0 +* Feature - Send a profile id for risk analysis model [#131](https://github.com/ebanx/benjamin/pull/131) + +## 1.9.1 +* Fix - Getting Pago Efectivo voucher now works as intented [#130](https://github.com/ebanx/benjamin/pull/130) + +## 1.9.0 +* Feature - Support for document type [#126](https://github.com/ebanx/benjamin/pull/126) +* Feature - Add method for canceling open payments [#128](https://github.com/ebanx/benjamin/pull/128) +* Feature - Add methods for validating if passed keys are valid [#129](https://github.com/ebanx/benjamin/pull/129) + +## 1.8.2 +* Fix - Send document also on LATAM countries requests [#123](https://github.com/ebanx/benjamin/pull/123) + +## 1.8.1 +* Fix - Put due date inside payment key on request payload [#119](https://github.com/ebanx/benjamin/pull/119) + +## 1.8.0 +* Feature - Added getTicketHtml method to main façade to work with printable gateway payments [#114](https://github.com/ebanx/benjamin/pull/114) + +## 1.7.0 +* Feature - Create method to translate country names into pay's required form [#111](https://github.com/ebanx/benjamin/pull/111) +* Fix - Missing Ecuador country code translation for API [#110](https://github.com/ebanx/benjamin/pull/110) + +## 1.6.0 +* Feature - Acquirer instalment limits for all creditcard currencies [#103](https://github.com/ebanx/benjamin/pull/103) +* Feature - Add creditcard on Argentina [#104](https://github.com/ebanx/benjamin/pull/104) +* Feature - Add safetypay to Ecuador [#105](https://github.com/ebanx/benjamin/pull/105) +* Feature - Spei isn't a Redirect method anymore [#106](https://github.com/ebanx/benjamin/pull/106) +* Feature - Redirect URL for hosted gateway request model [#108](https://github.com/ebanx/benjamin/pull/108) +* Fix - Check if country is in array before returning [#107](https://github.com/ebanx/benjamin/pull/107) + +## 1.5.0 +* Feature - Add address model to request [#101](https://github.com/ebanx/benjamin/pull/101) +* Feature - Hosted gateway full request support [#102](https://github.com/ebanx/benjamin/pull/102) + +## 1.4.1 +* Fix - Default http client not respecting connection mode set in config [#100](https://github.com/ebanx/benjamin/pull/100) + +## 1.4.0 +* Feature - Hosted payment gateway, request method is deprecated [#94](https://github.com/ebanx/benjamin/pull/94) +* Feature - ISO codes for country model [#97](https://github.com/ebanx/benjamin/pull/97) +* Fix - Internal gateway implementation of http service override [#95](https://github.com/ebanx/benjamin/pull/95) +* Fix - Code style [#96](https://github.com/ebanx/benjamin/pull/96) +* Fix - Credit card adapter optional field null reference error [#98](https://github.com/ebanx/benjamin/pull/98) + +## 1.3.0 +* Feature - Fetch rate is now a public method [#93](https://github.com/ebanx/benjamin/pull/93) +* Fix - Argentina can now process payments in local currency correctly [#92](https://github.com/ebanx/benjamin/pull/92) + +## 1.2.1 +* Added the ability to mock the API's http client for facade class as a means to avoid dependent projects to hit the API during their tests. [#90](https://github.com/ebanx/benjamin/pull/90) + +## 1.2.0 +* Feature - Merchant taxes flag support [#84](https://github.com/ebanx/benjamin/pull/84) +* Fix - Check if birthdate exists before formatting [#88](https://github.com/ebanx/benjamin/pull/88) +* Fix - Fix first instalment for credit card payments below minimum amount [#89](https://github.com/ebanx/benjamin/pull/89) + +## 1.1.0 +* Feature - Added SPEI as payment method on Mexico [#77](https://github.com/ebanx/benjamin/pull/77) +* Feature - Added Rapipago as payment method on Argentina [#78](https://github.com/ebanx/benjamin/pull/78) +* Feature - Added PagoFacil as payment method on Argentina [#79](https://github.com/ebanx/benjamin/pull/79) +* Feature - Added Otros Cupones as payment method on Argentina [#80](https://github.com/ebanx/benjamin/pull/80) + +## 1.0.0 +* First stable version diff --git a/lib/Ebanx/vendor/ebanx/benjamin/LICENSE b/lib/Ebanx/vendor/ebanx/benjamin/LICENSE new file mode 100644 index 00000000..9c8f3ea0 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/lib/Ebanx/vendor/ebanx/benjamin/README.md b/lib/Ebanx/vendor/ebanx/benjamin/README.md new file mode 100644 index 00000000..6a2ec774 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/README.md @@ -0,0 +1,50 @@ +# Benjamin +[![Build Status](https://img.shields.io/travis/ebanx/benjamin/master.svg?style=for-the-badge)](https://travis-ci.org/ebanx/benjamin) +[![codecov](https://img.shields.io/codecov/c/github/ebanx/benjamin/master.svg?style=for-the-badge)](https://codecov.io/gh/ebanx/benjamin) +[![Latest Stable Version](https://img.shields.io/packagist/v/ebanx/benjamin.svg?style=for-the-badge)](https://packagist.org/packages/ebanx/benjamin) +[![Total Downloads](https://img.shields.io/packagist/dt/ebanx/benjamin.svg?style=for-the-badge)](https://packagist.org/packages/ebanx/benjamin) +[![License](https://img.shields.io/packagist/l/ebanx/benjamin.svg?style=for-the-badge)](https://packagist.org/packages/ebanx/benjamin) + + +This is the repository for business rules as of implemented by merchant sites for use in e-commerce platform plugins. +The objective is to be a central repository for services and to communicate with the EBANX API (also known as "Pay"). + +## Getting Started + +It is very simple to use Benjamin. You will only need an instance of `Ebanx\Benjamin\Models\Configs\Config` and an instance of `Ebanx\Benjamin\Models\Payment`: + +```php + 'YOUR_INTEGRATION_KEY', + 'sandboxIntegrationKey' => 'YOUR_SANDBOX_INTEGRATION_KEY' +]); + +$payment = new Payment([ + //Payment properties(see wiki) +]); + +$result = EBANX($config)->create($payment); +``` + +If you want more information you can check the [Wiki](https://github.com/ebanx/benjamin/wiki/Using-Benjamin). + +## Contributing + +Check the [Wiki](https://github.com/ebanx/benjamin/wiki/Contributing). + +## License + +Copyright 2017 EBANX Payments + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use these files except in compliance with the License. +You may obtain a copy of the License at + + [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/Ebanx/vendor/ebanx/benjamin/composer.json b/lib/Ebanx/vendor/ebanx/benjamin/composer.json new file mode 100644 index 00000000..4fbe1975 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/composer.json @@ -0,0 +1,38 @@ +{ + "name": "ebanx/benjamin", + "description": "Business rule provider lib for plugin implementations", + "type": "library", + "license": "Apache-2.0", + "minimum-stability": "dev", + "require": { + "php": ">=5.4", + "guzzlehttp/guzzle": "^5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "fzaninotto/Faker": "^1.6@dev", + "justinrainbow/json-schema": "^5.0@dev", + "vlucas/phpdotenv": "^2.4@dev", + "sebastian/phpcpd": "^2.0.4", + "friendsofphp/php-cs-fixer": "^2.2.3" + }, + "autoload": { + "psr-4": { + "Ebanx\\Benjamin\\": "src/" + }, + "files": ["main.php"] + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "scripts": { + "test": "phpunit", + "test-only": "phpunit --filter", + "dup-check": "phpcpd src && phpcpd tests", + "test-all": "composer test && composer style-check && composer dup-check", + "style-check" : "php-cs-fixer fix --dry-run --verbose --diff ./", + "style-fix" : "php-cs-fixer fix --verbose ./" + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/composer.lock b/lib/Ebanx/vendor/ebanx/benjamin/composer.lock new file mode 100644 index 00000000..d57f8963 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/composer.lock @@ -0,0 +1,2676 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "35aa8721ddabeb6c1f2cbb095635e3d9", + "packages": [ + { + "name": "guzzlehttp/guzzle", + "version": "5.3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f9acb4761844317e626a32259205bec1f1bc60d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f9acb4761844317e626a32259205bec1f1bc60d2", + "reference": "f9acb4761844317e626a32259205bec1f1bc60d2", + "shasum": "" + }, + "require": { + "guzzlehttp/ringphp": "^1.1", + "php": ">=5.4.0", + "react/promise": "^2.2" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2018-01-15T07:18:01+00:00" + }, + { + "name": "guzzlehttp/ringphp", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/guzzle/RingPHP.git", + "reference": "4c8fe4c48a0fb7cc5e41ef529e43fecd6da4d539" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/4c8fe4c48a0fb7cc5e41ef529e43fecd6da4d539", + "reference": "4c8fe4c48a0fb7cc5e41ef529e43fecd6da4d539", + "shasum": "" + }, + "require": { + "guzzlehttp/streams": "~3.0", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", + "time": "2017-01-13T20:44:38+00:00" + }, + { + "name": "guzzlehttp/streams", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/guzzle/streams.git", + "reference": "d99a261c616210618ab94fd319cb17eda458cc3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/streams/zipball/d99a261c616210618ab94fd319cb17eda458cc3e", + "reference": "d99a261c616210618ab94fd319cb17eda458cc3e", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ], + "time": "2016-04-13T16:32:01+00:00" + }, + { + "name": "react/promise", + "version": "2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "179520ec0b25cf4c742d9653be9f4964d64d4a6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/179520ec0b25cf4c742d9653be9f4964d64d4a6e", + "reference": "179520ec0b25cf4c742d9653be9f4964d64d4a6e", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "time": "2017-09-19T13:02:44+00:00" + } + ], + "packages-dev": [ + { + "name": "composer/semver", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "2b303e43d14d15cc90c8e8db4a1cdb6259f1a5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/2b303e43d14d15cc90c8e8db4a1cdb6259f1a5c5", + "reference": "2b303e43d14d15cc90c8e8db4a1cdb6259f1a5c5", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "time": "2017-11-06T09:05:54+00:00" + }, + { + "name": "doctrine/annotations", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "9419fd5b220de184c9501a16ede49a029b94d482" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/9419fd5b220de184c9501a16ede49a029b94d482", + "reference": "9419fd5b220de184c9501a16ede49a029b94d482", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2018-02-04T20:16:08+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "870a62d7b0d63d4e0ffa8f2ce3ab7c8a53d1846d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/870a62d7b0d63d4e0ffa8f2ce3ab7c8a53d1846d", + "reference": "870a62d7b0d63d4e0ffa8f2ce3ab7c8a53d1846d", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-shim": "^0.9.2", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2018-03-05T09:41:42+00:00" + }, + { + "name": "doctrine/lexer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "cc709ba91eee09540091ad5a5f2616727662e41b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/cc709ba91eee09540091ad5a5f2616727662e41b", + "reference": "cc709ba91eee09540091ad5a5f2616727662e41b", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2017-07-24T09:37:08+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "c1a3c20197f8de5529e25aa800603c0d1ff69f51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c1a3c20197f8de5529e25aa800603c0d1ff69f51", + "reference": "c1a3c20197f8de5529e25aa800603c0d1ff69f51", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4", + "doctrine/annotations": "^1.2", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^5.6 || >=7.0 <7.3", + "php-cs-fixer/diff": "^1.3", + "symfony/console": "^3.2 || ^4.0", + "symfony/event-dispatcher": "^3.0 || ^4.0", + "symfony/filesystem": "^3.0 || ^4.0", + "symfony/finder": "^3.0 || ^4.0", + "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-php72": "^1.4", + "symfony/process": "^3.0 || ^4.0", + "symfony/stopwatch": "^3.0 || ^4.0" + }, + "conflict": { + "hhvm": "*" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", + "justinrainbow/json-schema": "^5.0", + "keradus/cli-executor": "^1.0", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.0", + "php-cs-fixer/accessible-object": "^1.0", + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "phpunitgoodpractices/traits": "^1.3.1", + "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.11-dev" + } + }, + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "classmap": [ + "tests/Test/AbstractFixerTestCase.php", + "tests/Test/AbstractIntegrationCaseFactory.php", + "tests/Test/AbstractIntegrationTestCase.php", + "tests/Test/Assert/AssertTokensTrait.php", + "tests/Test/Constraint/SameStringsConstraint.php", + "tests/Test/Constraint/SameStringsConstraintForV5.php", + "tests/Test/Constraint/SameStringsConstraintForV7.php", + "tests/Test/IntegrationCase.php", + "tests/Test/IntegrationCaseFactory.php", + "tests/Test/IntegrationCaseFactoryInterface.php", + "tests/Test/InternalIntegrationCaseFactory.php", + "tests/TestCase.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "time": "2018-03-21T17:41:56+00:00" + }, + { + "name": "fzaninotto/faker", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "65fbcca41437baff58a5c0e8f08fae617531dd0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/65fbcca41437baff58a5c0e8f08fae617531dd0d", + "reference": "65fbcca41437baff58a5c0e8f08fae617531dd0d", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "ext-intl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7", + "squizlabs/php_codesniffer": "^1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2018-03-15T16:58:08+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.x-dev", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "8560d4314577199ba51bf2032f02cd1315587c23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/8560d4314577199ba51bf2032f02cd1315587c23", + "reference": "8560d4314577199ba51bf2032f02cd1315587c23", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2018-02-14T22:26:30+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.11", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2017-09-27T21:40:39+00:00" + }, + { + "name": "php-cs-fixer/diff", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "SpacePossum" + } + ], + "description": "sebastian/diff v2 backport support for PHP5.6", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "time": "2018-02-15T16:58:55+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "81339187a96c6fdb70cd876b129891f8ca501508" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/81339187a96c6fdb70cd876b129891f8ca501508", + "reference": "81339187a96c6fdb70cd876b129891f8ca501508", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2018-02-14T18:58:54+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "182609736818dc750d42470c0be2a5ed74bad3bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/182609736818dc750d42470c0be2a5ed74bad3bd", + "reference": "182609736818dc750d42470c0be2a5ed74bad3bd", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "phpdocumentor/type-resolver": "^0", + "webmozart/assert": "^1" + }, + "require-dev": { + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2018-02-14T19:00:58+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "69bf1b199584f2004365a150c2e6cfbe852b6d66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/69bf1b199584f2004365a150c2e6cfbe852b6d66", + "reference": "69bf1b199584f2004365a150c2e6cfbe852b6d66", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "phpdocumentor/reflection-common": "^2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2018-02-14T18:59:20+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2018-02-19T10:16:54+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-10-06T15:47:00+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2017-11-27T13:52:08+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/9513098641797ce5f459dbc1de5a54c29b0ec1fb", + "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2018-01-06T05:27:16+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "58bd196ce8bc49389307b3787934a5117db80fea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/58bd196ce8bc49389307b3787934a5117db80fea", + "reference": "58bd196ce8bc49389307b3787934a5117db80fea", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-12-04T15:11:28+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "4.8.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "18e5f52e8412d23e739f7d8744e177039860e800" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/18e5f52e8412d23e739f7d8744e177039860e800", + "reference": "18e5f52e8412d23e739f7d8744e177039860e800", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.2.2", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.8.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2017-06-23T12:44:27+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02T06:51:40+00:00" + }, + { + "name": "psr/log", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "sebastian/comparator", + "version": "1.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "18a5d97c25f408f48acaf6d1b9f4079314c5996a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/18a5d97c25f408f48acaf6d1b9f4079314c5996a", + "reference": "18a5d97c25f408f48acaf6d1b9f4079314c5996a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2 || ~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2017-03-07T10:34:43+00:00" + }, + { + "name": "sebastian/diff", + "version": "1.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2017-05-22T07:24:03+00:00" + }, + { + "name": "sebastian/environment", + "version": "1.3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "67f55699c2810ff0f2cc47478bbdeda8567e68ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/67f55699c2810ff0f2cc47478bbdeda8567e68ee", + "reference": "67f55699c2810ff0f2cc47478bbdeda8567e68ee", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2017-02-28T08:18:59+00:00" + }, + { + "name": "sebastian/exporter", + "version": "1.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "dcd43bcc0fd3551bd2ede0081882d549bb78225d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/dcd43bcc0fd3551bd2ede0081882d549bb78225d", + "reference": "dcd43bcc0fd3551bd2ede0081882d549bb78225d", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0", + "sebastian/recursion-context": "^1.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2017-02-26T13:09:30+00:00" + }, + { + "name": "sebastian/finder-facade", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/finder-facade.git", + "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", + "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", + "shasum": "" + }, + "require": { + "symfony/finder": "~2.3|~3.0|~4.0", + "theseer/fdomdocument": "~1.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", + "homepage": "https://github.com/sebastianbergmann/finder-facade", + "time": "2017-11-18T17:31:49+00:00" + }, + { + "name": "sebastian/global-state", + "version": "1.1.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "cea85a84b00f2795341ebbbca4fa396347f2494e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/cea85a84b00f2795341ebbbca4fa396347f2494e", + "reference": "cea85a84b00f2795341ebbbca4fa396347f2494e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2|~5.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-02-23T14:11:06+00:00" + }, + { + "name": "sebastian/phpcpd", + "version": "2.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpcpd.git", + "reference": "24d9a880deadb0b8c9680e9cfe78e30b704225db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/24d9a880deadb0b8c9680e9cfe78e30b704225db", + "reference": "24d9a880deadb0b8c9680e9cfe78e30b704225db", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-timer": ">=1.0.6", + "sebastian/finder-facade": "~1.1", + "sebastian/version": "~1.0|~2.0", + "symfony/console": "~2.7|^3.0", + "theseer/fdomdocument": "~1.4" + }, + "bin": [ + "phpcpd" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Copy/Paste Detector (CPD) for PHP code.", + "homepage": "https://github.com/sebastianbergmann/phpcpd", + "time": "2016-04-17T19:32:49+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2016-10-03T07:41:43+00:00" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21T13:59:46+00:00" + }, + { + "name": "symfony/console", + "version": "3.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "23c06f589689aa3de8bbe314be04535bdb691e51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/23c06f589689aa3de8bbe314be04535bdb691e51", + "reference": "23c06f589689aa3de8bbe314be04535bdb691e51", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:32:39+00:00" + }, + { + "name": "symfony/debug", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "8c5df4183c9c4eafc130e6c9714b6eb15ce53aa1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/8c5df4183c9c4eafc130e6c9714b6eb15ce53aa1", + "reference": "8c5df4183c9c4eafc130e6c9714b6eb15ce53aa1", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": "<3.4" + }, + "require-dev": { + "symfony/http-kernel": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2018-03-19T22:38:22+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "606803460db12e1997cbde2504dfdd0175798885" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/606803460db12e1997cbde2504dfdd0175798885", + "reference": "606803460db12e1997cbde2504dfdd0175798885", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2018-03-09T17:46:25+00:00" + }, + { + "name": "symfony/filesystem", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "da8a42afd52b7652e626bafae09ed07c64ea3c30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/da8a42afd52b7652e626bafae09ed07c64ea3c30", + "reference": "da8a42afd52b7652e626bafae09ed07c64ea3c30", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2018-02-22T11:40:25+00:00" + }, + { + "name": "symfony/finder", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "6347bc280e751bb85b89cdbbda78ea25ffce2287" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/6347bc280e751bb85b89cdbbda78ea25ffce2287", + "reference": "6347bc280e751bb85b89cdbbda78ea25ffce2287", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2018-03-05T18:28:38+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "c2dd74950a0bab144ed5ee7e266b9795fc6fb230" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/c2dd74950a0bab144ed5ee7e266b9795fc6fb230", + "reference": "c2dd74950a0bab144ed5ee7e266b9795fc6fb230", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2018-01-18T22:19:50+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2018-01-30T19:27:44+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3532bfcd8f933a7816f3a0a59682fc404776600f", + "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2018-01-30T19:27:44+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "8eca20c8a369e069d4f4c2ac9895144112867422" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/8eca20c8a369e069d4f4c2ac9895144112867422", + "reference": "8eca20c8a369e069d4f4c2ac9895144112867422", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2018-01-31T17:43:24+00:00" + }, + { + "name": "symfony/process", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "8bd128b99d14270d267aa26ab45fd5c7d1b35bc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/8bd128b99d14270d267aa26ab45fd5c7d1b35bc3", + "reference": "8bd128b99d14270d267aa26ab45fd5c7d1b35bc3", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2018-03-22T13:45:58+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "07463bbbbbfe119045a24c4a516f92ebd2752784" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/07463bbbbbfe119045a24c4a516f92ebd2752784", + "reference": "07463bbbbbfe119045a24c4a516f92ebd2752784", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2018-02-19T16:51:42+00:00" + }, + { + "name": "symfony/yaml", + "version": "3.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2018-02-16T09:50:28+00:00" + }, + { + "name": "theseer/fdomdocument", + "version": "1.6.6", + "source": { + "type": "git", + "url": "https://github.com/theseer/fDOMDocument.git", + "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/6e8203e40a32a9c770bcb62fe37e68b948da6dca", + "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "lib-libxml": "*", + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "lead" + } + ], + "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", + "homepage": "https://github.com/theseer/fDOMDocument", + "time": "2017-06-30T11:53:12+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "fe2a5c22c94949983be10263417f47f8551d5946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/fe2a5c22c94949983be10263417f47f8551d5946", + "reference": "fe2a5c22c94949983be10263417f47f8551d5946", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "http://www.vancelucas.com" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2018-03-19T21:27:48+00:00" + }, + { + "name": "webmozart/assert", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-01-29T19:49:41+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": { + "fzaninotto/faker": 20, + "justinrainbow/json-schema": 20, + "vlucas/phpdotenv": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.4" + }, + "platform-dev": [] +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/main.php b/lib/Ebanx/vendor/ebanx/benjamin/main.php new file mode 100644 index 00000000..85602680 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/main.php @@ -0,0 +1,20 @@ + + + + tests + + + + + src + + + + + + + + diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Facade.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Facade.php new file mode 100644 index 00000000..017690c1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Facade.php @@ -0,0 +1,389 @@ +getShortClassName(); + call_user_func([$this, 'with'.$class], $config); + } + + return $this; + } + + /** + * @param Config $config + * @return Facade + */ + public function withConfig(Config $config) + { + $this->config = $config; + return $this; + } + + /** + * @param CreditCardConfig $creditCardConfig + * @return Facade + */ + public function withCreditCardConfig(CreditCardConfig $creditCardConfig) + { + $this->creditCardConfig = $creditCardConfig; + return $this; + } + + /** + * @param Payment $payment + * @return array + * @throws \InvalidArgumentException + */ + public function create(Payment $payment) + { + if ($payment->type === null) { + throw new \InvalidArgumentException('Invalid payment type'); + } + + if (!method_exists($this, $payment->type)) { + throw new \InvalidArgumentException('Invalid payment type'); + } + + $instance = call_user_func([$this, $payment->type]); + return $instance->create($payment); + } + + /** + * @param string $hash + * @return string + */ + public function getTicketHtml($hash) + { + $info = $this->paymentInfo()->findByHash($hash); + + $gatewayName = $this->getGatewayNameFromType($info['payment']['payment_type_code']); + if (!$gatewayName) { + return null; + } + + $gateway = $this->{$gatewayName}(); + if (!method_exists($gateway, 'getTicketHtml')) { + return null; + } + + return $gateway->getTicketHtml($hash); + } + + /** + * @param string $key + * + * @return bool + */ + public function isValidPrivateKey($key) + { + $data = ['integration_key' => $key]; + $response = $this->getHttpClient()->validatePrivateKey($data); + + return $response['status'] === 'SUCCESS'; + } + + /** + * @param string $key + * + * @return bool + * @throws \Exception + */ + public function isValidPublicKey($key) + { + $data = ['public_integration_key' => $key]; + try { + $response = $this->getHttpClient()->validatePublicKey($data); + + return $response['status'] === 'SUCCESS'; + } catch (\Exception $e) { + if ($e->getCode() === 409) { + return false; + } + + throw $e; + } + } + + # Gateways + + /** + * @return Gateways\Baloto + */ + public function baloto() + { + return new Gateways\Baloto($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Boleto + */ + public function boleto() + { + return new Gateways\Boleto($this->config, $this->getHttpClient()); + } + + /** + * @param CreditCardConfig $creditCardConfig (optional) credit card config + * @return Gateways\CreditCard + */ + public function creditCard(CreditCardConfig $creditCardConfig = null) + { + if ($creditCardConfig === null) { + $creditCardConfig = $this->creditCardConfig; + } + + return new Gateways\CreditCard($this->config, $creditCardConfig, $this->getHttpClient()); + } + + /** + * @return Gateways\Oxxo + */ + public function oxxo() + { + return new Gateways\Oxxo($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Spei + */ + public function spei() + { + return new Gateways\Spei($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Sencillito + */ + public function sencillito() + { + return new Gateways\Sencillito($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Webpay + */ + public function webpay() + { + return new Gateways\Webpay($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Multicaja + */ + public function multicaja() + { + return new Gateways\Multicaja($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\PagoEfectivo + */ + public function pagoEfectivo() + { + return new Gateways\PagoEfectivo($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Tef + */ + public function tef() + { + return new Gateways\Tef($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\EbanxAccount + */ + public function ebanxAccount() + { + return new Gateways\EbanxAccount($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Eft + */ + public function eft() + { + return new Gateways\Eft($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Servipag + */ + public function servipag() + { + return new Gateways\Servipag($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\DebitCard + */ + public function debitCard() + { + return new Gateways\DebitCard($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\SafetyPayCash + */ + public function safetyPayCash() + { + return new Gateways\SafetyPayCash($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\SafetyPayOnline + */ + public function safetyPayOnline() + { + return new Gateways\SafetyPayOnline($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Rapipago + */ + public function rapipago() + { + return new Gateways\Rapipago($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Pagofacil + */ + public function pagofacil() + { + return new Gateways\Pagofacil($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\OtrosCupones + */ + public function otrosCupones() + { + return new Gateways\OtrosCupones($this->config, $this->getHttpClient()); + } + + /** + * @return Gateways\Hosted + */ + public function hosted() + { + return new Gateways\Hosted($this->config, $this->getHttpClient()); + } + + /** + * @return PaymentInfo + */ + public function paymentInfo() + { + return new PaymentInfo($this->config, $this->getHttpClient()); + } + + /** + * @return Exchange + */ + public function exchange() + { + return new Exchange($this->config, $this->getHttpClient()); + } + + /** + * @return Refund + */ + public function refund() + { + return new Refund($this->config, $this->getHttpClient()); + } + + /** + * @return CancelPayment + */ + public function cancelPayment() + { + return new CancelPayment($this->config, $this->getHttpClient()); + } + + protected function getHttpClient() + { + if (is_null($this->httpClient)) { + $this->httpClient = new HttpClient(); + $this->httpClient->switchMode($this->config->isSandbox); + } + return $this->httpClient; + } + + protected function getGatewayNameFromType($apiType) + { + foreach ($this->getAllPublicServices() as $method => $service) { + $class = get_class($service); + + if (!defined($class.'::API_TYPE')) { + continue; + } + + if ($class::API_TYPE !== $apiType) { + continue; + } + + return $method; + } + + return null; + } + + protected function getAllPublicServices() + { + $methods = get_class_methods(get_class($this)); + $services = []; + + foreach ($methods as $method) { + $reflection = new \ReflectionMethod($this, $method); + + if (!$reflection->isPublic() + || $reflection->getNumberOfRequiredParameters() > 0) { + continue; + } + + $services[$method] = $this->{$method}(); + } + + return $services; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Address.php new file mode 100644 index 00000000..2cb18361 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Address.php @@ -0,0 +1,54 @@ + $value) { + if (!property_exists($this, $key)) { + continue; + } + + $this->{$key} = $value; + } + } + + /** + * @return string + */ + public function getShortClassname() + { + return basename(str_replace('\\', '/', get_class($this))); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Card.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Card.php new file mode 100644 index 00000000..4867c3e9 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Card.php @@ -0,0 +1,62 @@ + 5, + Currency::MXN => 100, + Currency::COP => 100, + Currency::ARS => 0, + ]; + + return isset($relation[$currency]) + ? $relation[$currency] + : null; + } + + /** + * Adds an interest rate config object for the credit card config. + * + * @param integer $instalmentNumber The instalment number for this rate configuration + * @param float $rate The interest rate to be applied + * @return CreditCardConfig itself + */ + public function addInterest($instalmentNumber, $rate) + { + $this->interestRates[] = new CreditCardInterestRateConfig([ + 'instalmentNumber' => $instalmentNumber, + 'interestRate' => $rate, + ]); + + return $this; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Configs/CreditCardInterestRateConfig.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Configs/CreditCardInterestRateConfig.php new file mode 100644 index 00000000..8b4f81a7 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Configs/CreditCardInterestRateConfig.php @@ -0,0 +1,21 @@ + self::ARGENTINA, + 'BR' => self::BRAZIL, + 'CL' => self::CHILE, + 'CO' => self::COLOMBIA, + 'EC' => self::ECUADOR, + 'MX' => self::MEXICO, + 'PE' => self::PERU + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Currency.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Currency.php new file mode 100644 index 00000000..03bce890 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Currency.php @@ -0,0 +1,60 @@ + self::ARS, + Country::BRAZIL => self::BRL, + Country::CHILE => self::CLP, + Country::COLOMBIA => self::COP, + Country::ECUADOR => self::USD, + Country::MEXICO => self::MXN, + Country::PERU => self::PEN, + ]; + + return $relation[$country]; + } + + public static function isGlobal($currency) + { + return in_array($currency, self::globalCurrencies()); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Item.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Item.php new file mode 100644 index 00000000..de0eb89e --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Item.php @@ -0,0 +1,47 @@ +code; + } + + /** + * Returns error message + * + * @return string + */ + public function getMessage() + { + return $this->message; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Responses/PaymentResponse.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Responses/PaymentResponse.php new file mode 100644 index 00000000..2da096f3 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/Responses/PaymentResponse.php @@ -0,0 +1,8 @@ +errors; + } + + /** + * @return mixed + */ + public function getBody() + { + return $this->body; + } + + /** + * @return bool + */ + public function hasErrors() + { + return count($this->errors) > 0; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Models/SubAccount.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/SubAccount.php new file mode 100644 index 00000000..2199534e --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Models/SubAccount.php @@ -0,0 +1,15 @@ +config->isSandbox + ? $this->config->sandboxIntegrationKey + : $this->config->integrationKey; + } + + protected function getNotificationUrl() + { + return isset($this->config->notificationUrl) + ? $this->config->notificationUrl + : ''; + } + + public function __construct(Config $config) + { + $this->config = $config; + } + + /** + * @return object + */ + abstract public function transform(); +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/BoletoPaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/BoletoPaymentAdapter.php new file mode 100644 index 00000000..8026a556 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/BoletoPaymentAdapter.php @@ -0,0 +1,16 @@ +payment->dueDate)) { + $transformed->due_date = $this->payment->dueDate->format('d/m/Y'); + } + + return $transformed; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/BrazilPaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/BrazilPaymentAdapter.php new file mode 100644 index 00000000..87c50697 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/BrazilPaymentAdapter.php @@ -0,0 +1,31 @@ +person_type = $this->payment->person->type; + + if ($this->payment->person->type === 'business') { + $transformed->responsible = $this->getResponsible(); + } + + return $transformed; + } + + private function getResponsible() + { + $payload = [ + 'name' => $this->payment->responsible->name, + 'document' => $this->payment->responsible->document, + ]; + + if (isset($this->payment->responsible->birthdate)) { + $payload['birth_date'] = $this->payment->responsible->birthdate->format('d/m/Y'); + } + + return (object) $payload; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CancelAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CancelAdapter.php new file mode 100644 index 00000000..6e38c1dc --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CancelAdapter.php @@ -0,0 +1,32 @@ +hash = $hash; + parent::__construct($config); + } + + public function transform() + { + return [ + 'integration_key' => $this->getIntegrationKey(), + 'hash' => $this->hash, + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CaptureAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CaptureAdapter.php new file mode 100644 index 00000000..27e6328b --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CaptureAdapter.php @@ -0,0 +1,41 @@ +data = $hash; + parent::__construct($config); + } + + public function transform() + { + $transformed = [ + 'integration_key' => $this->getIntegrationKey(), + 'merchant_capture_code' => $this->data['merchantCaptureCode'], + 'amount' => $this->data['amount'], + ]; + if (isset($this->data['hash'])) { + $transformed['hash'] = $this->data['hash']; + } + if (isset($this->data['merchantPaymentCode'])) { + $transformed['merchant_payment_code'] = $this->data['merchantPaymentCode']; + } + + return $transformed; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CardPaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CardPaymentAdapter.php new file mode 100644 index 00000000..a0f4e853 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CardPaymentAdapter.php @@ -0,0 +1,30 @@ +payment_type_code = $this->payment->card->type; + $transformed->create_token = $this->payment->card->createToken; + $transformed->token = $this->payment->card->token; + $transformed->instalments = $this->payment->instalments; + $transformed->creditcard = $this->transformCard(); + $transformed->device_id = $this->payment->deviceId; + + return $transformed; + } + + private function transformCard() + { + return (object) [ + 'card_number' => $this->payment->card->number, + 'card_name' => $this->payment->card->name, + 'card_due_date' => $this->payment->card->dueDate ? $this->payment->card->dueDate->format('m/Y') : null, + 'card_cvv' => $this->payment->card->cvv, + 'auto_capture' => $this->payment->card->autoCapture, + 'token' => $this->payment->card->token + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CashPaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CashPaymentAdapter.php new file mode 100644 index 00000000..b23fd20d --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/CashPaymentAdapter.php @@ -0,0 +1,16 @@ +payment->dueDate)) { + $transformed->due_date = $this->payment->dueDate->format('d/m/Y'); + } + + return $transformed; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/EftPaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/EftPaymentAdapter.php new file mode 100644 index 00000000..b9b0d95e --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/EftPaymentAdapter.php @@ -0,0 +1,13 @@ +eft_code = $this->payment->bankCode; + + return $transformed; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/ExchangeAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/ExchangeAdapter.php new file mode 100644 index 00000000..1eb714b2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/ExchangeAdapter.php @@ -0,0 +1,33 @@ +fromCurrency = $fromCurrency; + $this->toCurrency = $toCurrency; + parent::__construct($config); + } + + public function transform() + { + return [ + 'integration_key' => $this->getIntegrationKey(), + 'currency_code' => $this->fromCurrency, + 'currency_base_code' => $this->toCurrency, + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/PaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/PaymentAdapter.php new file mode 100644 index 00000000..38dcd6c1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/PaymentAdapter.php @@ -0,0 +1,109 @@ +payment = $payment; + parent::__construct($config); + } + + public function transform() + { + return (object) [ + 'integration_key' => $this->getIntegrationKey(), + 'operation' => 'request', + 'mode' => 'full', + 'metadata' => $this->transformMetadata(), + 'payment' => $this->transformPayment(), + ]; + } + + protected function transformPayment() + { + $userValues = array_replace( + $this->payment->userValues, + $this->config->userValues + ); + + $payload = [ + 'currency_code' => $this->config->baseCurrency, + 'notification_url' => $this->getNotificationUrl(), + 'redirect_url' => $this->config->redirectUrl, + 'name' => $this->payment->person->name, + 'email' => $this->payment->person->email, + 'amount_total' => $this->payment->amountTotal, + 'merchant_payment_code' => $this->payment->merchantPaymentCode, + 'order_number' => $this->payment->orderNumber, + 'customer_ip' => $this->payment->person->ip, + 'document' => $this->payment->person->document, + 'document_type' => $this->payment->person->documentType, + 'zipcode' => $this->payment->address->zipcode, + 'address' => $this->payment->address->address, + 'street_number' => $this->payment->address->streetNumber, + 'street_complement' => $this->payment->address->streetComplement, + 'city' => $this->payment->address->city, + 'state' => $this->payment->address->state, + 'country' => Country::toIso($this->payment->address->country), + 'phone_number' => $this->payment->person->phoneNumber, + 'note' => $this->payment->note, + 'items' => $this->transformItems(), + 'device_id' => $this->payment->deviceId, + 'payment_type_code' => $this->payment->type, + 'user_value_5' => 'Benjamin', + ]; + if ($birthdate = $this->payment->person->birthdate) { + $payload['birth_date'] = $birthdate->format('d/m/Y'); + } + + for ($i = 1; $i <= 4; $i++) { + if (!isset($userValues[$i])) { + continue; + } + + $payload['user_value_' . $i] = $userValues[$i]; + } + + return (object) $payload; + } + + protected function transformItems() + { + $itemArray = []; + + foreach ($this->payment->items as $item) { + $properties = [ + 'name' => $item->name, + 'description' => $item->description, + 'unit_price' => $item->unitPrice, + 'quantity' => $item->quantity, + 'type' => $item->type, + ]; + + $itemArray[] = (object) $properties; + } + + return (object) $itemArray; + } + + private function transformMetadata() + { + $metadata = [ + 'risk' => (object) [ + 'profile_id' => $this->payment->riskProfileId + ] + ]; + + return (object) $metadata; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/PaymentInfoAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/PaymentInfoAdapter.php new file mode 100644 index 00000000..c5850a56 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/PaymentInfoAdapter.php @@ -0,0 +1,39 @@ +type = $type; + $this->code = $code; + parent::__construct($config); + } + + public function transform() + { + return [ + 'integration_key' => $this->getIntegrationKey(), + $this->type => $this->code, + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/RefundAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/RefundAdapter.php new file mode 100644 index 00000000..d0e61cff --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/RefundAdapter.php @@ -0,0 +1,52 @@ +data = $hash; + parent::__construct($config); + } + + public function transform() + { + $transformed = [ + 'integration_key' => $this->getIntegrationKey(), + 'operation' => 'request', + 'amount' => $this->data['amount'], + 'description' => $this->data['description'], + ]; + if (isset($this->data['hash'])) { + $transformed['hash'] = $this->data['hash']; + } + + if (isset($this->data['merchantPaymentCode'])) { + $transformed['merchant_payment_code'] = $this->data['merchantPaymentCode']; + } + + return $transformed; + } + + public function transformCancel() + { + return [ + 'integration_key' => $this->getIntegrationKey(), + 'operation' => 'cancel', + 'refund_id' => $this->data['refundId'], + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/RequestAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/RequestAdapter.php new file mode 100644 index 00000000..ee7daa6a --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/RequestAdapter.php @@ -0,0 +1,117 @@ +request = $request; + parent::__construct($config); + } + + /** + * @return object + */ + public function transform() + { + $result = [ + 'integration_key' => $this->getIntegrationKey(), + 'currency_code' => $this->config->baseCurrency, + 'amount' => $this->request->amount, + 'merchant_payment_code' => $this->request->merchantPaymentCode, + 'order_number' => $this->request->orderNumber, + 'payment_type_code' => $this->request->type, + 'bypass_boleto_screen' => $this->request->skipThankyouPage, + 'due_date' => $this->transformDate($this->request->dueDate), + 'notification_url' => $this->getNotificationUrl(), + 'redirect_url' => $this->request->redirectUrl, + 'instalments' => implode('-', [ + $this->request->minInstalments, + $this->request->maxInstalments, + ]), + ]; + + $result = array_replace($result, $this->transformPerson($this->request->person)); + $result = array_replace($result, $this->transformAddress($this->request->address)); + $result = array_replace($result, $this->transformUserValues($this->request->userValues)); + $result = array_replace($result, $this->transformSubAccount($this->request->subAccount)); + + return (object) $result; + } + + protected function transformDate(\DateTime $date = null) + { + return isset($date) + ? $date->format('d/m/Y') + : null; + } + + protected function transformPerson(Person $person = null) + { + return [ + 'name' => $person->name, + 'email' => $person->email, + 'phone_number' => $person->phoneNumber, + 'person_type' => $person->type, + 'birth_date' => $person->birthdate, + ]; + } + + protected function transformAddress(Address $address = null) + { + return [ + 'country' => Country::toIso($address->country), + 'zipcode' => $address->zipcode, + 'address' => $address->address, + 'street_number' => $address->streetNumber, + 'street_complement' => $address->streetComplement, + 'city' => $address->city, + 'state' => $address->state, + ]; + } + + protected function transformUserValues(array $userValues = null) + { + $userValues = array_replace( + $userValues, + $this->config->userValues, + [5 => 'Benjamin'] + ); + + $result = []; + + for ($i = 1; $i <= 5; $i++) { + if (!isset($userValues[$i])) { + continue; + } + + $result['user_value_' . $i] = $userValues[$i]; + } + + return $result; + } + + protected function transformSubAccount(SubAccount $subAccount = null) + { + if (!$subAccount) { + return []; + } + + return [ + 'sub_acc_name' => $this->request->subAccount->name, + 'sub_acc_image_url' => $this->request->subAccount->imageUrl, + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/SafetyPayPaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/SafetyPayPaymentAdapter.php new file mode 100644 index 00000000..93eae7aa --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/SafetyPayPaymentAdapter.php @@ -0,0 +1,13 @@ +payment_type_code = substr_replace($this->payment->type, '-', 9, 0); + + return $transformed; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/TefPaymentAdapter.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/TefPaymentAdapter.php new file mode 100644 index 00000000..b586b858 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Adapters/TefPaymentAdapter.php @@ -0,0 +1,13 @@ +payment_type_code = $this->payment->bankCode; + + return $transformed; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/CancelPayment.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/CancelPayment.php new file mode 100644 index 00000000..ab4789ac --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/CancelPayment.php @@ -0,0 +1,21 @@ +config); + $response = $this->client->cancel($adapter->transform()); + + return $response; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Exchange.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Exchange.php new file mode 100644 index 00000000..3dc66836 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Exchange.php @@ -0,0 +1,82 @@ +fetchRate($this->config->baseCurrency, $localCurrency) * $siteValue; + } + + /** + * @param string $localCurrency Customer's currency code + * @param double $siteValue Value in shop currency + * @return double Converted local value with tax when it applies + */ + public function siteToLocalWithTax($localCurrency, $siteValue = 1) + { + $tax = 0.0; + + if ($localCurrency === Currency::BRL + && !$this->config->taxesOnMerchant) { + $tax = Config::IOF; + } + + return $this->siteToLocal($localCurrency, $siteValue) * (1 + $tax); + } + + /** + * @param string $localCurrency Customer's currency code + * @param double $localValue Value in customer's currency + * @return double Value in shop's currency + */ + public function localToSite($localCurrency, $localValue = 1) + { + return $this->fetchRate($localCurrency, $this->config->baseCurrency) * $localValue; + } + + /** + * @param string $fromCurrency + * @param string $toCurrency + * + * @return float + */ + public function fetchRate($fromCurrency, $toCurrency) + { + if ($fromCurrency === $toCurrency) { + return 1; + } + + $fromIsGlobal = Currency::isGlobal($fromCurrency); + $toIsGlobal = Currency::isGlobal($toCurrency); + + if (!($fromIsGlobal xor $toIsGlobal)) { + return 0; + } + + $adapter = new ExchangeAdapter( + $fromCurrency, + $toCurrency, + $this->config + ); + + $response = $this->client->exchange($adapter->transform()); + + if ($response['status'] === Client::ERROR) { + return 0; + } + + return $response['currency_rate']['rate']; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Baloto.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Baloto.php new file mode 100644 index 00000000..d73e2776 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Baloto.php @@ -0,0 +1,43 @@ +config); + return $adapter->transform(); + } + + /** + * @return string + */ + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/print/baloto/?hash=%s'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/BaseGateway.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/BaseGateway.php new file mode 100644 index 00000000..d8925413 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/BaseGateway.php @@ -0,0 +1,86 @@ +exchange = new Exchange($this->config, $this->client); + } + + /** + * @param string $country + * @return boolean + */ + public function isAvailableForCountry($country) + { + $siteCurrency = $this->config->baseCurrency; + $globalCurrencies = Currency::globalCurrencies(); + $localCurrency = Currency::localForCountry($country); + + $countryIsAccepted = static::acceptsCountry($country); + $currencyIsAccepted = static::acceptsCurrency($siteCurrency); + $siteCurrencyIsGlobal = in_array($siteCurrency, $globalCurrencies); + $siteCurrencyMatchesCountry = $siteCurrency === $localCurrency; + + return $countryIsAccepted + && $currencyIsAccepted + && ($siteCurrencyIsGlobal || $siteCurrencyMatchesCountry); + } + + /** + * @param string $currency + * @return bool + */ + public static function acceptsCurrency($currency) + { + return in_array($currency, static::getEnabledCurrencies()); + } + + /** + * @param string $country + * @return bool + */ + public static function acceptsCountry($country) + { + return in_array($country, static::getEnabledCountries()); + } + + protected function availableForCountryOrThrow($country) + { + if ($this->isAvailableForCountry($country)) { + return; + } + + throw new \InvalidArgumentException(sprintf( + 'Gateway not available for %s%s', + $country, + Currency::isGlobal($this->config->baseCurrency) + ? '' + : ' using '.$this->config->baseCurrency + )); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Boleto.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Boleto.php new file mode 100644 index 00000000..5babe66b --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Boleto.php @@ -0,0 +1,47 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/CreditCard.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/CreditCard.php new file mode 100644 index 00000000..edb4ee89 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/CreditCard.php @@ -0,0 +1,163 @@ +creditCardConfig = $creditCardConfig; + parent::__construct($config, $client); + } + + protected function getPaymentData(Payment $payment) + { + $this->availableForCountryOrThrow($payment->address->country); + + $payment->type = $payment->card->type; + + $adapter = new CardPaymentAdapter($payment, $this->config); + return $adapter->transform(); + } + + public function getMinInstalmentValueForCountry($country) + { + $this->availableForCountryOrThrow($country); + + $localCurrency = Currency::localForCountry($country); + $acquirerMinimum = CreditCardConfig::acquirerMinInstalmentValueForCurrency($localCurrency); + $configMinimum = $this->creditCardConfig->minInstalmentAmount; + + return max($acquirerMinimum, $configMinimum); + } + + public function getPaymentTermsForCountryAndValue($country, $value) + { + $this->availableForCountryOrThrow($country); + + $paymentTerms = []; + + $localCurrency = Currency::localForCountry($country); + $localValueWithTax = $this->exchange->siteToLocalWithTax($localCurrency, $value); + $minInstalment = $this->getMinInstalmentValueForCountry($country); + + // HARD LIMIT + $maxInstalments = min(CreditCardConfig::MAX_INSTALMENTS, $this->creditCardConfig->maxInstalments); + + for ($i = 1; $i <= $maxInstalments; $i++) { + $paymentTerms[] = $this->calculatePaymentTerm($i, $value, $localValueWithTax, $minInstalment); + } + + return array_filter($paymentTerms); + } + + /** + * @param string $hash + * @param float $amount + * @param string $merchantCaptureCode + * @return array + */ + public function captureByHash($hash, $amount = null, $merchantCaptureCode = null) + { + $data = [ + 'hash' => $hash, + 'amount' => $amount, + 'merchantCaptureCode' => $merchantCaptureCode, + ]; + + $adapter = new CaptureAdapter($data, $this->config); + $response = $this->client->capture($adapter->transform()); + + return $response; + } + + /** + * @param string $merchantPaymentCode + * @param float $amount + * @param string $merchantCaptureCode + * @return array + */ + public function captureByMerchantPaymentCode($merchantPaymentCode, $amount = null, $merchantCaptureCode = null) + { + $data = [ + 'merchantPaymentCode' => $merchantPaymentCode, + 'amount' => $amount, + 'merchantCaptureCode' => $merchantCaptureCode, + ]; + + $adapter = new CaptureAdapter($data, $this->config); + $response = $this->client->capture($adapter->transform()); + + return $response; + } + + private function calculatePaymentTerm($instalment, $siteValue, $localValueWithTax, $minimum) + { + $interestRates = $this->getInterestRates(); + + $interestRatio = 1 + (isset($interestRates[$instalment]) ? $interestRates[$instalment] / 100 : 0); + + if ($instalment !== 1 + && $localValueWithTax / $instalment * $interestRatio < $minimum) { + return null; + } + + return new PaymentTerm([ + 'instalmentNumber' => $instalment, + 'baseAmount' => ($siteValue / $instalment) * $interestRatio, + 'localAmountWithTax' => ($localValueWithTax / $instalment) * $interestRatio, + 'hasInterests' => $interestRatio > 1 + ]); + } + + private function getInterestRates() + { + if ($this->interestRates) { + return $this->interestRates; + } + + $this->interestRates = []; + foreach ($this->creditCardConfig->interestRates as $item) { + $this->interestRates[$item->instalmentNumber] = $item->interestRate; + } + + return $this->interestRates; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/DebitCard.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/DebitCard.php new file mode 100644 index 00000000..2c4be5e4 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/DebitCard.php @@ -0,0 +1,35 @@ +card->type = self::API_TYPE; + + $adapter = new CardPaymentAdapter($payment, $this->config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/DirectGateway.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/DirectGateway.php new file mode 100644 index 00000000..d0c896ee --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/DirectGateway.php @@ -0,0 +1,37 @@ +type = static::API_TYPE; + $body = $this->client->payment($this->getPaymentData($payment)); + + return $body; + } + + /** + * @deprecated 1.3.0 Payment requests should be made using Hosted gateway's create method + * @param Payment $payment + * @return array + */ + public function request(Payment $payment) + { + $payment->type = static::API_TYPE; + $body = $this->client->request($this->getPaymentData($payment)); + + return $body; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/EbanxAccount.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/EbanxAccount.php new file mode 100644 index 00000000..02600090 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/EbanxAccount.php @@ -0,0 +1,27 @@ +type = parent::API_TYPE; + $payment->bankCode = Bank::EBANX_ACCOUNT; + + return parent::getPaymentData($payment); + } + + protected static function getEnabledCurrencies() + { + return [ + Currency::BRL, + Currency::USD, + ]; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Eft.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Eft.php new file mode 100644 index 00000000..d03b4ed5 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Eft.php @@ -0,0 +1,32 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Hosted.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Hosted.php new file mode 100644 index 00000000..bd9a4951 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Hosted.php @@ -0,0 +1,36 @@ +client->request($this->getPaymentData($request)); + } + + protected static function getEnabledCountries() + { + return Country::all(); + } + + protected static function getEnabledCurrencies() + { + return Currency::all(); + } + + protected function getPaymentData(Request $request) + { + $adapter = new RequestAdapter($request, $this->config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Multicaja.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Multicaja.php new file mode 100644 index 00000000..87cbbb51 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Multicaja.php @@ -0,0 +1,31 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/OtrosCupones.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/OtrosCupones.php new file mode 100644 index 00000000..651deaed --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/OtrosCupones.php @@ -0,0 +1,43 @@ +config); + return $adapter->transform(); + } + + /** + * @return string + */ + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/print/voucher/?hash=%s'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Oxxo.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Oxxo.php new file mode 100644 index 00000000..6003a1ab --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Oxxo.php @@ -0,0 +1,43 @@ +config); + return $adapter->transform(); + } + + /** + * @return string + */ + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/print/oxxo/?hash=%s'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/PagoEfectivo.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/PagoEfectivo.php new file mode 100644 index 00000000..339088b8 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/PagoEfectivo.php @@ -0,0 +1,43 @@ +config); + return $adapter->transform(); + } + + /** + * @return string + */ + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/cip/?hash=%s'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Pagofacil.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Pagofacil.php new file mode 100644 index 00000000..4de1e1eb --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Pagofacil.php @@ -0,0 +1,43 @@ +config); + return $adapter->transform(); + } + + /** + * @return string + */ + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/print/voucher/?hash=%s'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Rapipago.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Rapipago.php new file mode 100644 index 00000000..73f130f0 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Rapipago.php @@ -0,0 +1,43 @@ +config); + return $adapter->transform(); + } + + /** + * @return string + */ + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/print/voucher/?hash=%s'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/SafetyPay.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/SafetyPay.php new file mode 100644 index 00000000..ef8bda3c --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/SafetyPay.php @@ -0,0 +1,33 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/SafetyPayCash.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/SafetyPayCash.php new file mode 100644 index 00000000..14936fd0 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/SafetyPayCash.php @@ -0,0 +1,7 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Servipag.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Servipag.php new file mode 100644 index 00000000..ed6aa5b1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Servipag.php @@ -0,0 +1,32 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Spei.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Spei.php new file mode 100644 index 00000000..2af3c57e --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Spei.php @@ -0,0 +1,43 @@ +config); + return $adapter->transform(); + } + + /** + * @return string + */ + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/print/spei/execute?hash=%s'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Tef.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Tef.php new file mode 100644 index 00000000..c84957e0 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Tef.php @@ -0,0 +1,31 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Webpay.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Webpay.php new file mode 100644 index 00000000..6b8518b2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Gateways/Webpay.php @@ -0,0 +1,31 @@ +config); + return $adapter->transform(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Http/Client.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Http/Client.php new file mode 100644 index 00000000..b1b69c60 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Http/Client.php @@ -0,0 +1,211 @@ +engine = new GuzzleHttp\Client(); + } + + protected function html($url) + { + return $this->engine->get($url)->getBody()->getContents(); + } + + /** + * @param object|array $data Any data you want to send + * @param string $endpoint The API endpoint you want to call + * @return array + */ + protected function post($data, $endpoint) + { + return $this->engine->post( + $this->getUrl() . $endpoint, + ['json' => $data] + )->json(); + } + + /** + * @param object|array $data Any data you want to send + * @param string $endpoint The API endpoint you want to call + * @return array + */ + protected function query($data, $endpoint) + { + return $this->engine->get( + $this->getUrl() . $endpoint, + ['query' => $data] + )->json(); + } + + /** + * @param object|array $data Payment data payload + * @return array + */ + public function payment($data) + { + return $this->post($data, 'ws/direct'); + } + + /** + * @param object|array $data Payment data payload + * @return array + */ + public function request($data) + { + return $this->post($data, 'ws/request'); + } + + /** + * @param object|array $data Payment data payload + * @return array + */ + public function refund($data) + { + return $this->query($data, 'ws/refund'); + } + + /** + * @param object|array $data Payment data payload + * @return array + */ + public function cancel($data) + { + return $this->query($data, 'ws/cancel'); + } + + /** + * @param object|array $data Payment data payload + * @return array + */ + public function capture($data) + { + return $this->query($data, 'ws/capture'); + } + + /** + * @param object|array $data Exchange data payload + * @return array + */ + public function exchange($data) + { + return $this->query($data, 'ws/exchange'); + } + + public function paymentInfo($data) + { + return $this->query($data, 'ws/query'); + } + + public function fetchContent($url) + { + return $this->html($url); + } + + /** + * @param object|array $data + * + * @return array + */ + public function validatePrivateKey($data) + { + return $this->query($data, 'ws/merchantIntegrationProperties/get'); + } + + /** + * @param object|array $data + * + * @return array + */ + public function validatePublicKey($data) + { + return $this->query($data, 'ws/merchantIntegrationProperties/isValidPublicIntegrationKey'); + } + + /** + * Current endpoint url + * + * @return string + */ + public function getUrl() + { + if ($this->mode === self::MODE_LIVE) { + return self::LIVE_URL; + } + + return self::SANDBOX_URL; + } + + /** + * Sets the client to sandbox mode + * + * @return Client + */ + public function inSandboxMode() + { + $this->mode = self::MODE_SANDBOX; + return $this; + } + + /** + * Sets the client to live mode + * + * @return Client + */ + public function inLiveMode() + { + $this->mode = self::MODE_LIVE; + return $this; + } + + /** + * @param bool $toSandbox Switch to sandbox(true) or live(false) modes + * @return void + */ + public function switchMode($toSandbox) + { + if ($toSandbox) { + return $this->inSandboxMode(); + } + + return $this->inLiveMode(); + } + + /** + * @return integer + */ + public function getMode() + { + return $this->mode; + } + + /** + * @return boolean + */ + public function isSandbox() + { + return $this->mode === self::MODE_SANDBOX; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Http/HttpService.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Http/HttpService.php new file mode 100644 index 00000000..99fed5c9 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Http/HttpService.php @@ -0,0 +1,34 @@ +config = $config; + $this->client = $this->client ?: $client; + + if (!$this->client) { + $this->client = new Client(); + $this->switchMode(null); + } + } + + /** + * @param bool|null $toSandbox Switch to default(null) sandbox(true) or live(false) modes + * @return void + */ + protected function switchMode($toSandbox) + { + if ($toSandbox === null) { + $toSandbox = $this->config->isSandbox; + } + + $this->client->switchMode($toSandbox); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/PaymentInfo.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/PaymentInfo.php new file mode 100644 index 00000000..35a334b8 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/PaymentInfo.php @@ -0,0 +1,47 @@ +fetchInfoByType('hash', $hash, $isSandbox); + } + + /** + * @param string $merchantPaymentCode + * @param bool|null $isSandbox + * @return array + */ + public function findByMerchantPaymentCode($merchantPaymentCode, $isSandbox = null) + { + return $this->fetchInfoByType('merchant_payment_code', $merchantPaymentCode, $isSandbox); + } + + /** + * @param string $type Search type + * @param string $query Search key + * @param bool|null $isSandbox + * @return array + */ + private function fetchInfoByType($type, $query, $isSandbox) + { + $adapter = new PaymentInfoAdapter($type, $query, $this->config); + + $this->switchMode($isSandbox); + $response = $this->client->paymentInfo($adapter->transform()); + $this->switchMode(null); + + //TODO: decorate response + return $response; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Refund.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Refund.php new file mode 100644 index 00000000..acd99799 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Refund.php @@ -0,0 +1,63 @@ + $hash, + 'amount' => $amount, + 'description' => $description, + ]; + + return $this->request($data); + } + + /** + * @param string $merchantPaymentCode The merchant payment code + * @param float $amount The amount to be refunded; expressed in the original payment currency. + * @param string $description Description of the refund reason. + * @return array + */ + public function requestByMerchantPaymentCode($merchantPaymentCode, $amount, $description) + { + $data = [ + 'merchantPaymentCode' => $merchantPaymentCode, + 'amount' => $amount, + 'description' => $description, + ]; + + return $this->request($data); + } + + public function cancel($refundId) + { + $adapter = new RefundAdapter(['refundId' => $refundId], $this->config); + $response = $this->client->refund($adapter->transformCancel()); + + return $response; + } + + /** + * @param $data + * @return array + */ + private function request($data) + { + $adapter = new RefundAdapter($data, $this->config); + $response = $this->client->refund($adapter->transform()); + + return $response; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Traits/Printable.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Traits/Printable.php new file mode 100644 index 00000000..4e187ed0 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Traits/Printable.php @@ -0,0 +1,43 @@ +getUrlFormat(), $this->getDomain($isSandbox), $hash); + } + + /** + * @param string $hash + * @param bool $isSandbox + * @return string + */ + public function getTicketHtml($hash, $isSandbox = null) + { + return $this->client->fetchContent($this->getUrl($hash, $isSandbox)); + } + + /** + * @param bool $isSandbox + * @return string + */ + private function getDomain($isSandbox = null) + { + if ($isSandbox === null) { + $isSandbox = $this->config->isSandbox; + } + + return $isSandbox ? 'sandbox' : 'print'; + } + + /** + * @return string + */ + abstract protected function getUrlFormat(); +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Validators/BaseValidator.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Validators/BaseValidator.php new file mode 100644 index 00000000..8f94fbe8 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Validators/BaseValidator.php @@ -0,0 +1,38 @@ +config = $config; + } + + public function getErrors() + { + return $this->errors; + } + + public function hasErrors() + { + return count($this->errors) > 0; + } + + protected function addAllErrors($errors = []) + { + $this->errors = array_merge($this->errors, $errors); + } + + protected function addError($message) + { + $this->errors[] = $message; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Validators/ValidationHelper.php b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Validators/ValidationHelper.php new file mode 100644 index 00000000..b2a838c7 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/src/Services/Validators/ValidationHelper.php @@ -0,0 +1,55 @@ +appliedRules[] = function ($subjectName, $subjectValue) use ($minValue) { + if ($subjectValue >= $minValue) { + return null; + } + + return sprintf( + '%s is below minimum of %d', + $subjectName, + $minValue + ); + }; + + return $this; + } + + public function max($maxValue) + { + $this->appliedRules[] = function ($subjectName, $subjectValue) use ($maxValue) { + if ($subjectValue <= $maxValue) { + return null; + } + + return sprintf( + '%s is above maximum of %d', + $subjectName, + $maxValue + ); + }; + + return $this; + } + + public function test($subjectName, $subjectValue) + { + $errors = []; + + while ($rule = array_shift($this->appliedRules)) { + $errors[] = call_user_func_array($rule, [$subjectName, $subjectValue]); + } + + return array_values(array_filter($errors)); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/BaseBuilder.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/BaseBuilder.php new file mode 100644 index 00000000..5f7eafe1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/BaseBuilder.php @@ -0,0 +1,22 @@ +faker = $faker; + $this->instance = $instance; + } + + public function build() + { + return $this->instance; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/BuilderFactory.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/BuilderFactory.php new file mode 100644 index 00000000..3a6c7c3e --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/BuilderFactory.php @@ -0,0 +1,91 @@ +lang = $lang; + } + + /** + * @param Payment $instance Optional pre-built instance + * @return PaymentBuilder + */ + public function payment(Payment $instance = null) + { + return new PaymentBuilder($this->setupFaker(), $instance); + } + + /** + * @param Request $instance Optionl pre-built instance + * @return RequestBuilder + */ + public function request(Request $instance = null) + { + return new RequestBuilder($this->setupFaker(), $instance); + } + + /** + * @param string $lang + * + * @return BuilderFactory + */ + public function withLang($lang) + { + $this->lang = $lang; + return $this; + } + + /** + * @return Faker\Generator + */ + private function setupFaker() + { + if (!$this->faker || $this->fakerLang != $this->lang) { + $this->fakerLang = $this->lang; + + $localProviderDir = 'Tests\Helpers\Providers\\'.$this->lang; + + $this->faker = Faker\Factory::create(self::convertLangToFakerLang($this->lang)); + $this->faker->addProvider(new Providers\CurrencyCode($this->faker)); + $this->faker->addProvider(new Providers\SubAccount($this->faker)); + $this->faker->addProvider(new Providers\Item($this->faker)); + $this->faker->addProvider(new Providers\Payment($this->faker)); + $this->faker->addProvider(new Providers\Card($this->faker)); + $this->faker->addProvider(new Providers\Request($this->faker)); + + $personProviderClass = $localProviderDir.'\Person'; + $this->faker->addProvider(new $personProviderClass($this->faker)); + + $documentProviderClass = $localProviderDir.'\Document'; + $this->faker->addProvider(new $documentProviderClass($this->faker)); + + $addressProviderClass = $localProviderDir.'\Address'; + $this->faker->addProvider(new $addressProviderClass($this->faker)); + } + $this->faker->seed('ebanx'); + + return $this->faker; + } + + private static function convertLangToFakerLang($lang) + { + if ($lang === 'pt_BR') { + return $lang; + } + return 'es_ES'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/PaymentBuilder.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/PaymentBuilder.php new file mode 100644 index 00000000..526c8ba7 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/PaymentBuilder.php @@ -0,0 +1,107 @@ +paymentModel(); + } + + parent::__construct($faker, $instance); + } + + /** + * @return Payment + */ + public function build() + { + return $this->instance; + } + + public function businessPerson() + { + $this->instance->person = $this->faker->businessPersonModel(); + $this->instance->responsible = $this->faker->personModel(); + + return $this; + } + + public function boleto() + { + $this->instance->type = 'boleto'; + $this->instance->dueDate = $this->faker->dateTimeBetween('+1 days', '+3 days'); + + return $this; + } + + public function baloto() + { + $this->instance->type = 'baloto'; + $this->instance->dueDate = $this->faker->dateTimeBetween('+1 days', '+3 days'); + + return $this; + } + + public function creditCard($instalmentNumber = 1) + { + $this->instance->type = 'creditcard'; + $this->instance->card = $this->faker->cardModel(); + $this->instance->instalments = $instalmentNumber; + + return $this; + } + + public function emptyCreditCard($instalmentNumber = 1) + { + $this->instance->type = 'creditcard'; + $this->instance->card = new Card(); + $this->instance->instalments = $instalmentNumber; + + return $this; + } + + public function debitCard() + { + $this->instance->type = 'debitcard'; + $this->instance->card = $this->faker->cardModel(); + $this->instance->card->number = '4242424242424242'; + + return $this; + } + + public function tef() + { + $this->instance->type = 'tef'; + $this->instance->bankCode = Bank::BANCO_DO_BRASIL; + + return $this; + } + + public function eft() + { + $this->instance->type = 'eft'; + $this->instance->bankCode = 'banco_gnb_sudameris'; + + return $this; + } + + public function flow() + { + $this->instance->type = 'flowcl'; + $this->instance->flow_payment_method = 'webpay'; + + return $this; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/RequestBuilder.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/RequestBuilder.php new file mode 100644 index 00000000..f063ccc5 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Builders/RequestBuilder.php @@ -0,0 +1,35 @@ +requestModel(); + } + + parent::__construct($faker, $instance); + } + + public function withSubAccount() + { + $this->instance->subAccount = $this->faker->subAccountModel(); + return $this; + } + + /** + * @return Request + */ + public function build() + { + return $this->instance; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Environment.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Environment.php new file mode 100644 index 00000000..23a0abd2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Environment.php @@ -0,0 +1,29 @@ +load(); + } + + /** + * @param string $key Env var name to read + * @param mixed $default Optional default value + * @return string + */ + public function read($key, $default = self::ENV_VAR_NOT_SET) + { + $result = getenv($key) ?: $default; + if ($result === self::ENV_VAR_NOT_SET) { + throw new \InvalidArgumentException('No such env var '.$key.' found!'); + } + + return $result; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Mocks/Http/ClientForTests.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Mocks/Http/ClientForTests.php new file mode 100644 index 00000000..251ce77d --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Mocks/Http/ClientForTests.php @@ -0,0 +1,22 @@ +engine = $engine; + } + } + + public function withEngine($engine) + { + $this->engine = $engine; + return $this; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Mocks/Http/EchoEngine.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Mocks/Http/EchoEngine.php new file mode 100644 index 00000000..50a3eeb3 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Mocks/Http/EchoEngine.php @@ -0,0 +1,117 @@ +baseUrl = $baseUrl; + + if (!is_array($responses)) { + $this->responses = new EchoEngineResponse($responses); + return; + } + + foreach ($responses as $key => $value) { + $this->responses[$key] = new EchoEngineResponse($value); + } + } + + /** + * @param string $url Url to request from + * @param array $options + * @return EchoEngineResponse + */ + public function post($url = null, array $options = []) + { + return $this->fakeResponse($url); + } + + /** + * @param string $url Url to request from + * @param array $options + * @return EchoEngineResponse + */ + public function get($url = null, $options = []) + { + return $this->fakeResponse($url); + } + + private function fakeResponse($url) + { + if (!is_array($this->responses)) { + return $this->responses; + } + + $endpoint = str_replace($this->baseUrl, '', $url); + if ($this->responses[$endpoint]->json()['status'] === 'CONFLICT') { + throw new ClientException('Conflict: invalid key', new \GuzzleHttp\Message\Request('GET', $endpoint), new Response(409)); + } + if ($this->responses[$endpoint]->json()['status'] === 'NOT FOUND') { + throw new ClientException('Any other http status returned', new \GuzzleHttp\Message\Request('GET', $endpoint), new Response(404)); + } + return $this->responses[$endpoint]; + } +} + +class EchoEngineResponse +{ + /** + * @var string + */ + private $response = ''; + + /** + * @param string $response What to respond + */ + public function __construct($response) + { + $this->response = $response; + } + + /** + * @return string + */ + public function json() + { + return json_decode($this->response, true); + } + + public function getBody() + { + return new EchoEngineResponseBody($this->response); + } +} + +class EchoEngineResponseBody +{ + private $response; + + public function __construct($response) + { + $this->response = $response; + } + public function getContents() + { + return $this->response; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Address.php new file mode 100644 index 00000000..65160721 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Address.php @@ -0,0 +1,25 @@ +address = $this->faker->streetName; + $address->city = $this->faker->city; + $address->country = Country::BRAZIL; + $address->state = $this->faker->stateAbbr(); + $address->streetComplement = $this->faker->secondaryAddress(); + $address->streetNumber = $this->faker->buildingNumber; + $address->zipcode = $this->faker->postcode; + + return $address; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/BaseProvider.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/BaseProvider.php new file mode 100644 index 00000000..934ff54a --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/BaseProvider.php @@ -0,0 +1,14 @@ +faker = $faker; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Card.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Card.php new file mode 100644 index 00000000..63990157 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Card.php @@ -0,0 +1,24 @@ +createToken = false; + $card->name = $this->faker->name(); + $card->dueDate = $this->faker->creditCardExpirationDate(); + $card->autoCapture = true; + $card->cvv = '123'; + $card->number = $this->faker->creditCardNumber(); + $card->type = $this->faker->creditCardType(); + + return $card; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/CurrencyCode.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/CurrencyCode.php new file mode 100644 index 00000000..49137cc0 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/CurrencyCode.php @@ -0,0 +1,33 @@ +faker->randomElement([ + CurrencyModel::USD, + CurrencyModel::EUR, + CurrencyModel::BRL, + CurrencyModel::MXN, + CurrencyModel::PEN, + CurrencyModel::COP, + CurrencyModel::CLP, + ]); + } + + /** + * @return string + */ + public function invalidEbanxCurrencyCode() + { + return self::INVALID; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Item.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Item.php new file mode 100644 index 00000000..5c22ddcf --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Item.php @@ -0,0 +1,39 @@ +name = $this->faker->words(2, true); + $item->description = $this->faker->text(140); + $item->type = $this->faker->word; + $item->sku = strtoupper($this->faker->bothify('?-???-######')); + + $item->unitPrice = $this->faker->randomFloat(2, 1, 10); + $item->quantity = $this->faker->numberBetween(1, 3); + + return $item; + } + + /** + * @param integer $count + * @return ItemModel[] + */ + public function itemModels($count) + { + $result = []; + + for ($i = 0; $i < $count; $i++) { + $result[] = $this->itemModel(); + } + + return $result; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Payment.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Payment.php new file mode 100644 index 00000000..cfbbbd9f --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Payment.php @@ -0,0 +1,27 @@ +address = $this->faker->addressModel(); + $payment->person = $this->faker->personModel(); + $payment->deviceId = $this->faker->sha256; + $payment->merchantPaymentCode = md5(time()); + $payment->note = 'Fake payment created by PHPUnit.'; + $payment->items = $this->faker->itemModels($this->faker->randomDigitNotNull()); + $payment->amountTotal = array_reduce($payment->items, function ($carry, $item) { + $carry += $item->unitPrice * $item->quantity; + return $carry; + }); + + return $payment; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Person.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Person.php new file mode 100644 index 00000000..3f37e3b5 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Person.php @@ -0,0 +1,35 @@ +birthdate = $this->faker->dateTimeBetween('-65 years', '-18 years'); + $person->email = $this->faker->email; + $person->ip = $this->faker->ipv4; + $person->name = $this->faker->name; + $person->phoneNumber = $this->faker->phoneNumber; + $person->document = $this->faker->documentNumber(false); + + return $person; + } + + /** + * @return \Ebanx\Benjamin\Models\Person + */ + public function businessPersonModel() + { + $person = $this->personModel(); + $person->type = PersonModel::TYPE_BUSINESS; + $person->document = $this->faker->cnpj(false); + + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Request.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Request.php new file mode 100644 index 00000000..b4d38c57 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/Request.php @@ -0,0 +1,21 @@ +address = $this->faker->addressModel(); + $request->person = $this->faker->personModel(); + $request->amount = $this->faker->randomFloat(2, 1, 10); + $request->merchantPaymentCode = md5(time()); + + return $request; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/SubAccount.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/SubAccount.php new file mode 100644 index 00000000..b09f94ca --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/SubAccount.php @@ -0,0 +1,19 @@ +name = $this->faker->name; + $subAccount->imageUrl = $this->faker->url; + + return $subAccount; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_AR/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_AR/Address.php new file mode 100644 index 00000000..3ba62935 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_AR/Address.php @@ -0,0 +1,21 @@ +country = Country::ARGENTINA; + + return $model; + } + + public function stateAbbr() + { + return ''; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_AR/Document.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_AR/Document.php new file mode 100644 index 00000000..d9c33120 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_AR/Document.php @@ -0,0 +1,12 @@ +email .= '.ar'; + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Address.php new file mode 100644 index 00000000..5773ac41 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Address.php @@ -0,0 +1,21 @@ +country = Country::CHILE; + + return $model; + } + + public function stateAbbr() + { + return ''; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Document.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Document.php new file mode 100644 index 00000000..c82390d4 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Document.php @@ -0,0 +1,12 @@ +faker->randomNumber(9, true); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Person.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Person.php new file mode 100644 index 00000000..4cbf0748 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CL/Person.php @@ -0,0 +1,17 @@ +email .= '.cl'; + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Address.php new file mode 100644 index 00000000..53cfdfba --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Address.php @@ -0,0 +1,21 @@ +country = Country::COLOMBIA; + + return $model; + } + + public function stateAbbr() + { + return ''; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Document.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Document.php new file mode 100644 index 00000000..da6fda55 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Document.php @@ -0,0 +1,12 @@ +faker->randomNumber(9, true); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Person.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Person.php new file mode 100644 index 00000000..e7a74cf0 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_CO/Person.php @@ -0,0 +1,17 @@ +email .= '.co'; + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_EC/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_EC/Address.php new file mode 100644 index 00000000..7351809f --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_EC/Address.php @@ -0,0 +1,21 @@ +country = Country::ECUADOR; + + return $model; + } + + public function stateAbbr() + { + return ''; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_EC/Document.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_EC/Document.php new file mode 100644 index 00000000..31fc3d86 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_EC/Document.php @@ -0,0 +1,12 @@ +email .= '.ec'; + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Address.php new file mode 100644 index 00000000..957165c2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Address.php @@ -0,0 +1,21 @@ +country = Country::MEXICO; + + return $model; + } + + public function stateAbbr() + { + return ''; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Document.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Document.php new file mode 100644 index 00000000..6fd22d39 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Document.php @@ -0,0 +1,12 @@ +faker->randomNumber(9, true); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Person.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Person.php new file mode 100644 index 00000000..5349f0a8 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_MX/Person.php @@ -0,0 +1,17 @@ +email .= '.mx'; + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_PE/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_PE/Address.php new file mode 100644 index 00000000..8587a032 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_PE/Address.php @@ -0,0 +1,21 @@ +country = Country::PERU; + + return $model; + } + + public function stateAbbr() + { + return ''; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_PE/Document.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_PE/Document.php new file mode 100644 index 00000000..4e6e413a --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/es_PE/Document.php @@ -0,0 +1,12 @@ +email .= '.pe'; + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/pt_BR/Address.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/pt_BR/Address.php new file mode 100644 index 00000000..a5b881f2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/pt_BR/Address.php @@ -0,0 +1,8 @@ +faker->cnpj($format); + } + + public function documentNumber($format = true) + { + return $this->faker->cpf($format); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/pt_BR/Person.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/pt_BR/Person.php new file mode 100644 index 00000000..a36ccd28 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Helpers/Providers/pt_BR/Person.php @@ -0,0 +1,17 @@ +email .= '.br'; + return $person; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Integration/FacadeTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Integration/FacadeTest.php new file mode 100644 index 00000000..4bd881f9 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Integration/FacadeTest.php @@ -0,0 +1,417 @@ +assertNotNull($ebanx); + + return $ebanx; + } + + /** + * @param Facade $ebanx + * + * @throws \ReflectionException + * @depends testMainObject + */ + public function testGatewayAccessors($ebanx) + { + $gateways = $this->getExpectedGateways(); + + foreach ($gateways as $gateway) { + $class = new \ReflectionClass('Ebanx\Benjamin\Services\Gateways\\' . ucfirst($gateway)); + + // skip abstract gateways + if ($class->isAbstract()) { + continue; + } + + $this->assertAccessor($ebanx, $gateway); + } + } + + /** + * @param $ebanx + * + * @throws \ReflectionException + * @depends testMainObject + */ + public function testOtherServicesAccessors($ebanx) + { + $services = $this->getExpectedServices(); + + foreach ($services as $service) { + $class = new \ReflectionClass('Ebanx\Benjamin\Services\\' . ucfirst($service)); + + // skip abstract gateways + if ($class->isAbstract()) { + continue; + } + + $this->assertAccessor($ebanx, $service); + } + } + + /** + * @param Facade $ebanx + * @depends testMainObject + * @expectedException \InvalidArgumentException + */ + public function testCreatePaymentWithoutPaymentType($ebanx) + { + $ebanx->create(new Payment()); + } + + /** + * @param Facade $ebanx + * @depends testMainObject + * @expectedException \InvalidArgumentException + */ + public function testCreatePaymentWithWrongPaymentType($ebanx) + { + $ebanx->create(new Payment([ + 'type' => 'invalidType', + ])); + } + + /** + * @param Facade $ebanx + * @depends testMainObject + */ + public function testCreatePaymentByFacade($ebanx) + { + $ebanx = new FacadeForTests(); + $ebanx->addConfig(new Config()); + + $result = $ebanx->create(new Payment([ + 'type' => 'test', + ])); + + $this->assertArrayHasKey('payment', $result); + } + + public function testDefaultClientMode() + { + $ebanx = new FacadeForTests(); + $ebanx->addConfig( + new Config([ + 'isSandbox' => false + ]), + new CreditCardConfig() + ); + + $this->assertFalse( + $ebanx->getHttpClient()->isSandbox(), + 'Client connection mode is ignoring config' + ); + } + + public function testCheckValidPrivateKey() + { + $integrationKey = 'testing'; + $privateKeyUrl = 'ws/merchantIntegrationProperties/get'; + + $ebanx = $this->buildMockedFacade([ + $privateKeyUrl => $this->buildPrivateKeyValidationMock($integrationKey), + ]); + + $subject = $ebanx->isValidPrivateKey($integrationKey); + + $this->assertTrue($subject); + } + + public function testCheckInvalidPrivateKey() + { + $integrationKey = 'invalid'; + $privateKeyUrl = 'ws/merchantIntegrationProperties/get'; + + $ebanx = $this->buildMockedFacade([ + $privateKeyUrl => $this->buildPrivateKeyValidationMock($integrationKey), + ]); + + $subject = $ebanx->isValidPrivateKey($integrationKey); + + $this->assertFalse($subject); + } + + /** + * @throws \Exception Won't be thrown in this test + */ + public function testCheckValidPublicKey() + { + $integrationKey = 'testing'; + $privateKeyUrl = 'ws/merchantIntegrationProperties/isValidPublicIntegrationKey'; + + $ebanx = $this->buildMockedFacade([ + $privateKeyUrl => $this->buildPublicKeyValidationMock($integrationKey), + ]); + + $subject = $ebanx->isValidPublicKey($integrationKey); + + $this->assertTrue($subject); + } + + /** + * @throws \Exception Won't be thrown in this test + */ + public function testCheckInvalidPublicKey() + { + $integrationKey = 'invalidKey'; + $privateKeyUrl = 'ws/merchantIntegrationProperties/isValidPublicIntegrationKey'; + + $ebanx = $this->buildMockedFacade([ + $privateKeyUrl => $this->buildPublicKeyValidationMock($integrationKey), + ]); + + $subject = $ebanx->isValidPublicKey($integrationKey); + + $this->assertFalse($subject); + } + + /** + * @expectedException \Exception + */ + public function testCheckPublicKeyWithOtherWrongResponse() + { + $integrationKey = 'invalidKey'; + $publicKeyUrl = 'ws/merchantIntegrationProperties/isValidPublicIntegrationKey'; + + $ebanx = $this->buildMockedFacade([ + $publicKeyUrl => '{"status": "NOT FOUND"}', + ]); + + $ebanx->isValidPublicKey($integrationKey); + } + + /** + * @param Facade $ebanx + * + * @depends testMainObject + */ + public function testGetTicketHtmlForAllAPIs($ebanx) + { + $hash = md5(rand()); + $expected = "$hash"; + $infoUrl = 'ws/query'; + $gateways = []; + + foreach ($this->getExpectedGateways() as $gateway) { + if (method_exists($ebanx, $gateway)) { + $gateways[] = $ebanx->{$gateway}(); + } + } + foreach ($gateways as $gateway) { + $class = get_class($gateway); + + if (!defined($class.'::API_TYPE') + || !in_array('Ebanx\Benjamin\Services\Traits\Printable', class_uses($class))) { + continue; + } + + $url = str_replace('https://sandbox.ebanx.com/', '', $gateway->getUrl($hash)); + + $facade = $this->buildMockedFacade([ + $infoUrl => $this->buildPaymentInfoMock($hash, $class::API_TYPE), + $url => $expected . $class, + ]); + + $response = $facade->getTicketHtml($hash); + + $this->assertEquals($expected . $class, $response); + } + } + + + public function testGetTicketHtmlWithBadPaymentType() + { + $hash = md5(rand()); + $infoUrl = 'ws/query'; + $printUrl = "print/?hash=$hash"; + + $ebanx = $this->buildMockedFacade([ + $infoUrl => $this->buildPaymentInfoMock($hash, 'none'), + $printUrl => "$hash", + ]); + + $subject = $ebanx->getTicketHtml($hash); + + $this->assertNull($subject); + } + + public function testGetTicketHtmlWithNonPrintableGateway() + { + $hash = md5(rand()); + $infoUrl = 'ws/query'; + $printUrl = "print/?hash=$hash"; + + $ebanx = $this->buildMockedFacade([ + $infoUrl => $this->buildPaymentInfoMock($hash, 'tef'), + $printUrl => "$hash", + ]); + + $subject = $ebanx->getTicketHtml($hash); + + $this->assertNull($subject); + } + + private function getExpectedGateways() + { + $result = []; + + $dir = opendir('src/Services/Gateways'); + while (($file = readdir($dir)) !== false) { + + // skip non-php files + if ($file === basename($file, '.php')) { + continue; + } + + $result[] = lcfirst(basename($file, '.php')); + } + closedir($dir); + + return $result; + } + + private function getExpectedServices() + { + $result = []; + + $dir = opendir('src/Services'); + while (($file = readdir($dir)) !== false) { + + // skip non-php files + if ($file === basename($file, '.php')) { + continue; + } + + $result[] = lcfirst(basename($file, '.php')); + } + closedir($dir); + + return $result; + } + + private function buildMockedFacade($responseMock = null) + { + $ebanx = new FacadeForTests(); + $ebanx->addConfig(new Config()); + $ebanx->addConfig(new CreditCardConfig()); + + if ($responseMock) { + $ebanx->setHttpClient($this->getMockedClient($responseMock)); + } + + return $ebanx; + } + + private function buildPaymentInfoMock($hash, $type = 'test') + { + return '{"payment":{"hash":"'.$hash.'","payment_type_code":"'.$type.'"},"status":"SUCCESS"}'; + } + + private function buildPrivateKeyValidationMock($key) + { + if ($key === 'testing') { + return '{ + "status": "SUCCESS", + "success": true, + "resource": "merchantIntegrationProperties", + "operation": "get", + "body": { + "image": "", + "url_response": "www.ebanx.com", + "receipt_email": null, + "url_status_change_notification": "www.ebanx.com" + } + }'; + } + return '{ + "status": "ERROR", + "status_code": "BP-SA-2", + "status_message": "Invalid integration key" + }'; + } + + private function buildPublicKeyValidationMock($key) + { + if ($key === 'testing') { + return '{ + "status": "SUCCESS", + "success": true, + "resource": "merchantIntegrationProperties", + "operation": "isValidPublicIntegrationKey", + "body": [] + }'; + } + return '{ + "status": "CONFLICT", + "success": false, + "resource": "merchantIntegrationProperties", + "operation": "isValidPublicIntegrationKey", + "body": { + "error": "Invalid public_integration_key" + } + }'; + } + + private function assertAccessor($facade, $name) + { + $this->assertTrue( + method_exists($facade, $name), + 'Accessor method not defined' + ); + + $this->assertNotNull( + $facade->{$name}(), + 'Accessor returned null service' + ); + } +} + +class GatewayForTests extends BaseGateway +{ + use Printable; + + const API_TYPE = 'test'; + + public function create() + { + return ['payment' => []]; + } + + protected function getUrlFormat() + { + return 'https://%s.ebanx.com/print/?hash=%s'; + } +} + +class FacadeForTests extends Facade +{ + public function test() + { + return new GatewayForTests($this->config, $this->getHttpClient()); + } + + public function getHttpClient() + { + return parent::getHttpClient(); + } + + public function setHttpClient(HttpClient $client) + { + $this->httpClient = $client; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/TestCase.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/TestCase.php new file mode 100644 index 00000000..89322be1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/TestCase.php @@ -0,0 +1,14 @@ + 'bar', + 'baz' => 'qux' + ]; + $subModel = new BaseModelImplementation($testData); + + $this->assertObjectHasAttribute('foo', $subModel); + $this->assertEquals('bar', $subModel->foo); + $this->assertObjectNotHasAttribute('baz', $subModel); + } +} + +class BaseModelImplementation extends BaseModel +{ + public $foo; +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Configs/CreditCardConfigTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Configs/CreditCardConfigTest.php new file mode 100644 index 00000000..1d34f661 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Configs/CreditCardConfigTest.php @@ -0,0 +1,50 @@ +addInterest(1, 0.02) + ->addInterest(2, 0.06) + ->addInterest(3, 0.10) + ->addInterest(4, 0.15); + + $this->assertEquals(4, count($ccConfig->interestRates)); + } + + public function testAcquirerMinInstalmentValueForCurrency() + { + $countries = CreditCardForTests::getEnabledCountries(); + foreach ($countries as $country) { + $currency = Currency::localForCountry($country); + $this->assertNotNull( + CreditCardConfig::acquirerMinInstalmentValueForCurrency($currency), + 'CreditCardConfig has no acquirer minimum instalment value set for '.$currency + ); + } + } + + public function testAcquirerMinInstalmentValueForCurrencyWithInvalidCode() + { + $this->assertNull( + CreditCardConfig::acquirerMinInstalmentValueForCurrency('AAA'), + 'Invalid currency should not have a value' + ); + } +} + +class CreditCardForTests extends CreditCard +{ + public static function getEnabledCountries() + { + return parent::getEnabledCountries(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/CountryTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/CountryTest.php new file mode 100644 index 00000000..3ef6d259 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/CountryTest.php @@ -0,0 +1,32 @@ +assertEquals(Country::ARGENTINA, Country::fromIso('AR')); + $this->assertEquals(Country::BRAZIL, Country::fromIso('BR')); + $this->assertEquals(Country::CHILE, Country::fromIso('CL')); + $this->assertEquals(Country::COLOMBIA, Country::fromIso('CO')); + $this->assertEquals(Country::ECUADOR, Country::fromIso('EC')); + $this->assertEquals(Country::MEXICO, Country::fromIso('MX')); + $this->assertEquals(Country::PERU, Country::fromIso('PE')); + $this->assertNull(Country::fromIso('ZZ')); + } + + public function testCountryToIsoCodes() + { + $this->assertEquals('AR', Country::toIso(Country::ARGENTINA)); + $this->assertEquals('BR', Country::toIso(Country::BRAZIL)); + $this->assertEquals('CL', Country::toIso(Country::CHILE)); + $this->assertEquals('CO', Country::toIso(Country::COLOMBIA)); + $this->assertEquals('EC', Country::toIso(Country::ECUADOR)); + $this->assertEquals('MX', Country::toIso(Country::MEXICO)); + $this->assertEquals('PE', Country::toIso(Country::PERU)); + $this->assertNull(Country::toIso('Ebanxland')); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/CurrencyTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/CurrencyTest.php new file mode 100644 index 00000000..c35bddf1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/CurrencyTest.php @@ -0,0 +1,13 @@ +assertNull(Currency::localForCountry('invalidCountry')); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Responses/ErrorResponseTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Responses/ErrorResponseTest.php new file mode 100644 index 00000000..7d0d840c --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Responses/ErrorResponseTest.php @@ -0,0 +1,18 @@ + 0, + 'message' => 'test', + ]); + + $this->assertEquals(0, $error->getCode()); + $this->assertEquals('test', $error->getMessage()); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Responses/ResponseTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Responses/ResponseTest.php new file mode 100644 index 00000000..9a4c8ca2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Models/Responses/ResponseTest.php @@ -0,0 +1,22 @@ + [ + new ErrorResponse(), + new ErrorResponse(), + ], + 'body' => 'test', + ]); + + $this->assertCount(2, $response->getErrors()); + $this->assertEquals('test', $response->getBody()); + $this->assertTrue($response->hasErrors()); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/BoletoPaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/BoletoPaymentAdapterTest.php new file mode 100644 index 00000000..52b98e4c --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/BoletoPaymentAdapterTest.php @@ -0,0 +1,64 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->boleto()->businessPerson()->build(); + + $adapter = new BoletoPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator; + $validator->validate($result, $this->getSchema(['paymentSchema', 'brazilPaymentSchema', 'cashPaymentSchema'])); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } + + public function testDueDateIsInsidePayment() + { + $payment = new Payment([ + 'dueDate' => new \DateTime(), + 'person' => new Person(), + 'address' => new Address(), + ]); + + $adapter = new BoletoPaymentAdapter($payment, new Config()); + $result = $adapter->transform(); + + $this->assertObjectHasAttribute('due_date', $result->payment); + } + + public function testRequestAttributeNumber() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->boleto()->businessPerson()->build(); + + $adapter = new BoletoPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $numberOfKeys = count((array) $result); + $this->assertEquals(5, $numberOfKeys); + $this->assertObjectHasAttribute('integration_key', $result); + $this->assertObjectHasAttribute('operation', $result); + $this->assertObjectHasAttribute('mode', $result); + $this->assertObjectHasAttribute('metadata', $result); + $this->assertObjectHasAttribute('payment', $result); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/BrazilPaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/BrazilPaymentAdapterTest.php new file mode 100644 index 00000000..93631482 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/BrazilPaymentAdapterTest.php @@ -0,0 +1,31 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->boleto()->businessPerson()->build(); + + $adapter = new BrazilFakeAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator; + $validator->validate($result, $this->getSchema(['paymentSchema', 'brazilPaymentSchema'])); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } +} + +class BrazilFakeAdapter extends BrazilPaymentAdapter +{ +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/CardPaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/CardPaymentAdapterTest.php new file mode 100644 index 00000000..16957ae2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/CardPaymentAdapterTest.php @@ -0,0 +1,61 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->creditCard()->businessPerson()->build(); + + $adapter = new CardPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator; + $validator->validate($result, $this->getSchema(['paymentSchema', 'brazilPaymentSchema', 'cardPaymentSchema'])); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } + + public function testAdaptEmptyCard() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->emptyCreditCard()->businessPerson()->build(); + + $adapter = new CardPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $this->assertObjectHasAttribute('payment', $result); + } + + public function testRequestAttributeNumber() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->emptyCreditCard()->businessPerson()->build(); + + $adapter = new CardPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $numberOfKeys = count((array) $result); + $this->assertEquals(5, $numberOfKeys); + $this->assertObjectHasAttribute('integration_key', $result); + $this->assertObjectHasAttribute('operation', $result); + $this->assertObjectHasAttribute('mode', $result); + $this->assertObjectHasAttribute('metadata', $result); + $this->assertObjectHasAttribute('payment', $result); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/CashPaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/CashPaymentAdapterTest.php new file mode 100644 index 00000000..2e41bd44 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/CashPaymentAdapterTest.php @@ -0,0 +1,64 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('es_MX'); + $payment = $factory->payment()->baloto()->build(); + + $adapter = new CashPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator; + $validator->validate($result, $this->getSchema(['paymentSchema', 'cashPaymentSchema'])); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } + + public function testDueDateIsInsidePayment() + { + $payment = new Payment([ + 'dueDate' => new \DateTime(), + 'person' => new Person(), + 'address' => new Address(), + ]); + + $adapter = new CashPaymentAdapter($payment, new Config()); + $result = $adapter->transform(); + + $this->assertObjectHasAttribute('due_date', $result->payment); + } + + public function testRequestAttributeNumber() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('es_MX'); + $payment = $factory->payment()->baloto()->build(); + + $adapter = new CashPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $numberOfKeys = count((array) $result); + $this->assertEquals(5, $numberOfKeys); + $this->assertObjectHasAttribute('integration_key', $result); + $this->assertObjectHasAttribute('operation', $result); + $this->assertObjectHasAttribute('mode', $result); + $this->assertObjectHasAttribute('metadata', $result); + $this->assertObjectHasAttribute('payment', $result); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/EftPaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/EftPaymentAdapterTest.php new file mode 100644 index 00000000..0797c5f1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/EftPaymentAdapterTest.php @@ -0,0 +1,47 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('es_CO'); + $payment = $factory->payment()->eft()->build(); + + $adapter = new EftPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator; + $validator->validate($result, $this->getSchema(['paymentSchema'])); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } + + public function testRequestAttributeNumber() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('es_CO'); + $payment = $factory->payment()->eft()->build(); + + $adapter = new EftPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $numberOfKeys = count((array) $result); + $this->assertEquals(5, $numberOfKeys); + $this->assertObjectHasAttribute('integration_key', $result); + $this->assertObjectHasAttribute('operation', $result); + $this->assertObjectHasAttribute('mode', $result); + $this->assertObjectHasAttribute('metadata', $result); + $this->assertObjectHasAttribute('payment', $result); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/PaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/PaymentAdapterTest.php new file mode 100644 index 00000000..6ec79dd4 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/PaymentAdapterTest.php @@ -0,0 +1,222 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->boleto()->businessPerson()->build(); + + $adapter = new FakeAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator(); + $validator->validate($result, $this->getSchema('paymentSchema')); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } + + public function testIntegrationKey() + { + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + + $liveKey = 'testIntegrationKey'; + $sandboxKey = 'testSandboxIntegrationKey'; + + $config = new Config([ + 'integrationKey' => $liveKey, + 'sandboxIntegrationKey' => $sandboxKey + ]); + + // Sandbox + $adapter = new FakeAdapter($payment, $config); + $this->assertEquals($sandboxKey, $adapter->getIntegrationKey()); + + // Live + $config->isSandbox = false; + $adapter = new FakeAdapter($payment, $config); + $this->assertEquals($liveKey, $adapter->getIntegrationKey()); + } + + public function testUserValues() + { + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + + $expected = [ + 1 => 'from_tests', + 2 => 'DO NOT PAY', + 5 => 'Benjamin', + ]; + + $payment->userValues = [ + 1 => 'Override me', + 2 => 'DO NOT PAY', + ]; + + $config = new Config([ + 'userValues' => [ + 1 => 'from_tests', + ], + ]); + + $adapter = new FakeAdapter($payment, $config); + $result = $adapter->transform(); + + $resultValues = array_filter([ + 1 => isset($result->payment->user_value_1) ? $result->payment->user_value_1 : null, + 2 => isset($result->payment->user_value_2) ? $result->payment->user_value_2 : null, + 3 => isset($result->payment->user_value_3) ? $result->payment->user_value_3 : null, + 4 => isset($result->payment->user_value_4) ? $result->payment->user_value_4 : null, + 5 => isset($result->payment->user_value_5) ? $result->payment->user_value_5 : null, + ]); + + $this->assertEquals($expected, $resultValues); + } + + public function testSiteCurrencyCOP() + { + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + + $config = new Config([ + 'baseCurrency' => Currency::COP + ]); + + $adapter = new FakeAdapter($payment, $config); + $result = $adapter->transform(); + + $this->assertEquals(Currency::COP, $result->payment->currency_code); + } + public function testSiteCurrencyEUR() + { + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + + $config = new Config([ + 'baseCurrency' => Currency::EUR + ]); + + $adapter = new FakeAdapter($payment, $config); + $result = $adapter->transform(); + + $this->assertEquals(Currency::EUR, $result->payment->currency_code); + } + + public function testTransformDocument() + { + $document = '123456789'; + $adapter = new FakeAdapter(new Payment([ + 'person' => new Person(['document' => $document]), + 'address' => new Address() + ]), new Config()); + $result = $adapter->transform(); + + $this->assertEquals($document, $result->payment->document); + } + + public function testTransformWithoutDocument() + { + $adapter = new FakeAdapter(new Payment([ + 'person' => new Person(), + 'address' => new Address() + ]), new Config()); + $result = $adapter->transform(); + + $this->assertObjectHasAttribute('document', $result->payment); + } + + public function testTransformDocumentType() + { + $documentType = Person::DOCUMENT_TYPE_ARGENTINA_CDI; + $adapter = new FakeAdapter(new Payment([ + 'person' => new Person(['documentType' => $documentType]), + 'address' => new Address() + ]), new Config()); + $result = $adapter->transform(); + + $this->assertEquals($documentType, $result->payment->document_type); + } + + public function testTransformWithoutDocumentType() + { + $adapter = new FakeAdapter(new Payment([ + 'person' => new Person(), + 'address' => new Address() + ]), new Config()); + $result = $adapter->transform(); + + $this->assertObjectHasAttribute('document_type', $result->payment); + } + + public function testTransformProfileId() + { + $riskProfileId = 'Bx1x0x0'; + $adapter = new FakeAdapter(new Payment([ + 'person' => new Person(), + 'address' => new Address(), + 'riskProfileId' => $riskProfileId, + ]), new Config()); + $result = $adapter->transform(); + + $this->assertEquals($riskProfileId, $result->metadata->risk->profile_id); + } + + public function testTransformWithoutProfileId() + { + $adapter = new FakeAdapter(new Payment([ + 'person' => new Person(), + 'address' => new Address(), + ]), new Config()); + $result = $adapter->transform(); + + $this->assertObjectHasAttribute('profile_id', $result->metadata->risk); + } + + protected function getJsonMessage(JsonSchema\Validator $validator) + { + $message = ''; + $message .= "JSON does not validate. Violations:\n"; + foreach ($validator->getErrors() as $error) { + $message .= sprintf("[%s] %s\n", $error['property'], $error['message']); + } + return $message; + } + + protected function getSchema($schemas) + { + if (!is_array($schemas)) { + $schemas = [$schemas]; + } + + $object = []; + foreach ($schemas as $schema) { + $object = array_merge_recursive($object, json_decode(file_get_contents(dirname(__DIR__) . '/Adapters/Schemas/' . $schema . '.json'), true)); + } + + return json_decode(json_encode($object)); + } +} + +class FakeAdapter extends PaymentAdapter +{ + public function getIntegrationKey() + { + return parent::getIntegrationKey(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/RequestAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/RequestAdapterTest.php new file mode 100644 index 00000000..88c3b88f --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/RequestAdapterTest.php @@ -0,0 +1,180 @@ +request() + ->build(); + + $this->assertModelJsonSchemaCompliance($request); + } + + public function testJsonSchemaWithSubAccount() + { + $factory = new BuilderFactory('pt_BR'); + $request = $factory + ->request() + ->withSubAccount() + ->build(); + + $this->assertModelJsonSchemaCompliance($request); + } + + public function testTransformNotificationUrl() + { + $expected = md5(rand(1, 999)); + + $nullConfig = new Config(); + $goodConfig = new Config(['notificationUrl' => $expected]); + + $factory = new BuilderFactory('pt_BR'); + $request = $factory->request()->build(); + + $adapter = new FakeRequestAdapter($request, $nullConfig); + $result1 = $adapter->transform(); + + $adapter = new FakeRequestAdapter($request, $goodConfig); + $result2 = $adapter->transform(); + + $this->assertEmpty( + $result1->notification_url, + 'Request adapter injected a notification url when it shouldn\'t' + ); + + $this->assertEquals( + $expected, + $result2->notification_url, + 'Request adapter failed to inject a notification url' + ); + } + + public function testTransformRedirectUrl() + { + $expected = 'SAMPLE_URL'; + + $factory = new BuilderFactory('pt_BR'); + $request = $factory->request()->build(); + $request->redirectUrl = $expected; + + $adapter = new FakeRequestAdapter($request, new Config()); + $result = $adapter->transform(); + + $this->assertEquals( + $expected, + $result->redirect_url, + 'Request adapter failed to send redirect_url' + ); + } + + public function testIntegrationKey() + { + $factory = new BuilderFactory('pt_BR'); + $request = $factory->request()->build(); + + $liveKey = 'testIntegrationKey'; + $sandboxKey = 'testSandboxIntegrationKey'; + + $config = new Config([ + 'integrationKey' => $liveKey, + 'sandboxIntegrationKey' => $sandboxKey + ]); + + // Sandbox + $adapter = new FakeRequestAdapter($request, $config); + $this->assertEquals($sandboxKey, $adapter->getIntegrationKey()); + + // Live + $config->isSandbox = false; + $adapter = new FakeRequestAdapter($request, $config); + $this->assertEquals($liveKey, $adapter->getIntegrationKey()); + } + + public function testUserValues() + { + $factory = new BuilderFactory('pt_BR'); + $request = $factory->request()->build(); + + $expected = [ + 1 => 'from_tests', + 2 => 'DO NOT PAY', + 5 => 'Benjamin', + ]; + + $request->userValues = [ + 1 => 'Override me', + 2 => 'DO NOT PAY', + ]; + + $config = new Config([ + 'userValues' => [ + 1 => 'from_tests', + ], + ]); + + $adapter = new FakeRequestAdapter($request, $config); + $result = $adapter->transform(); + + $resultValues = array_filter([ + 1 => isset($result->user_value_1) ? $result->user_value_1 : null, + 2 => isset($result->user_value_2) ? $result->user_value_2 : null, + 3 => isset($result->user_value_3) ? $result->user_value_3 : null, + 4 => isset($result->user_value_4) ? $result->user_value_4 : null, + 5 => isset($result->user_value_5) ? $result->user_value_5 : null, + ]); + + $this->assertEquals($expected, $resultValues); + } + + public function testAddress() + { + $factory = new BuilderFactory('pt_BR'); + $request = $factory->request()->build(); + + $expected = 'Rua Marechal Deodoro'; + $request->address = new Address([ + 'address' => $expected, + 'country' => Country::BRAZIL + ]); + + $adapter = new FakeRequestAdapter($request, new Config()); + $result = $adapter->transform(); + + $this->assertEquals($expected, $result->address); + } + + private function assertModelJsonSchemaCompliance($model) + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + + $adapter = new FakeRequestAdapter($model, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator(); + $validator->validate($result, $this->getSchema('requestSchema')); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } +} + +class FakeRequestAdapter extends RequestAdapter +{ + public function getIntegrationKey() + { + return parent::getIntegrationKey(); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/SafetyPayPaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/SafetyPayPaymentAdapterTest.php new file mode 100644 index 00000000..49aa7f25 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/SafetyPayPaymentAdapterTest.php @@ -0,0 +1,93 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + + $adapter = new SafetyPayPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator; + $validator->validate($result, $this->getSchema(['paymentSchema'])); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } + + public function testCashTypeCode() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + $payment->type = 'SafetyPayCash'; + + $adapter = new SafetyPayPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $this->assertEquals('safetypay-cash', strtolower($result->payment->payment_type_code)); + } + + public function testOnlineTypeCode() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + $payment->type = 'SafetyPayOnline'; + + $adapter = new SafetyPayPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $this->assertEquals('safetypay-online', strtolower($result->payment->payment_type_code)); + } + + public function testOnEcuador() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('es_EC'); + $payment = $factory->payment()->build(); + $payment->type = 'SafetyPayOnline'; + + $adapter = new SafetyPayPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $this->assertEquals('ec', strtolower($result->payment->country)); + } + + public function testRequestAttributeNumber() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + $payment->type = 'SafetyPayCash'; + + $adapter = new SafetyPayPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $numberOfKeys = count((array) $result); + $this->assertEquals(5, $numberOfKeys); + $this->assertObjectHasAttribute('integration_key', $result); + $this->assertObjectHasAttribute('operation', $result); + $this->assertObjectHasAttribute('mode', $result); + $this->assertObjectHasAttribute('metadata', $result); + $this->assertObjectHasAttribute('payment', $result); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/brazilPaymentSchema.json b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/brazilPaymentSchema.json new file mode 100644 index 00000000..bc1318bd --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/brazilPaymentSchema.json @@ -0,0 +1,38 @@ +{ + "properties": { + "payment": { + "properties": { + "person_type": { + "type": "string", + "pattern": "^(personal)|(business)$" + }, + "currency_code": { + "pattern": "^(BRL)|(USD)|(EUR)$" + }, + "document": { + "type": "string", + "pattern": "^([0-9]{2}[\\.]?[0-9]{3}[\\.]?[0-9]{3}[\\/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[\\.]?[0-9]{3}[\\.]?[0-9]{3}[-]?[0-9]{2})$", + "required": true + }, + "birth_date": { + "required": true + }, + "zipcode": { + "pattern": "^[0-9]{5}-?[0-9]{3}$", + "required": true + }, + "address": { + "required": true + }, + "street_number": { + "required": true + }, + "state": { + "maxLength": 2, + "minLength": 2, + "required": true + } + } + } + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/cardPaymentSchema.json b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/cardPaymentSchema.json new file mode 100644 index 00000000..e18dbd67 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/cardPaymentSchema.json @@ -0,0 +1,122 @@ +{ + "properties": { + "payment": { + "properties": { + "create_token": { + "type": "boolean" + }, + "token": { + "minLength": 32, + "maxLength": 128 + }, + "instalments": { + "type": "integer", + "minimum": 1, + "maximum": 12 + }, + "creditcard": { + "type": "object", + "properties": { + "card_number": { + "type": "string", + "minLength": 14, + "maxLength": 19, + "required": true + }, + "card_name": { + "type": "string", + "maxLength": 50, + "required": true + }, + "card_due_date": { + "type": "string", + "pattern": "^[0-9]{2}/[0-9]{4}$", + "required": true + }, + "card_cvv": { + "type": "string", + "pattern": "^[0-9]{3,4}$", + "required": true + }, + "auto_capture": { + "type": "boolean" + }, + "token": { + "maxLength": 200 + }, + "note": { + "type": "string", + "maxLength": 200 + }, + "responsible": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "document": { + "type": "string", + "pattern": "^([0-9]{2}[\\.]?[0-9]{3}[\\.]?[0-9]{3}[\\/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[\\.]?[0-9]{3}[\\.]?[0-9]{3}[-]?[0-9]{2})$" + }, + "birth_date": { + "type": "string", + "pattern": "^[0-9]{2}/[0-9]{2}/[0-9]{4}$" + } + } + }, + "sub_account": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 32 + }, + "image_url": { + "type": "string", + "maxLength": 200, + "pattern": "^https:" + } + } + }, + "items": { + "type": "object", + "properties": { + "sku": { + "type": "string", + "maxLength": 20 + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "unit_price": { + "type": "number" + }, + "quantity": { + "type": "integer" + }, + "type": { + "type": "string", + "maxLength": 50 + } + } + }, + "device_id": { + "type": "string", + "maxLength": 200 + }, + "notification_url": { + "type": "string", + "maxLength": 2000 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/cashPaymentSchema.json b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/cashPaymentSchema.json new file mode 100644 index 00000000..f1706f0b --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/cashPaymentSchema.json @@ -0,0 +1,12 @@ +{ + "properties": { + "payment": { + "properties": { + "due_date": { + "type": "string", + "pattern": "^[0-9]{2}/[0-9]{2}/[0-9]{4}$" + } + } + } + } +} \ No newline at end of file diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/paymentSchema.json b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/paymentSchema.json new file mode 100644 index 00000000..c7be8b2b --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/paymentSchema.json @@ -0,0 +1,123 @@ +{ + "type": "object", + "properties": { + "integration_key": { + "type": "string", + "maxLength": 100, + "required": true + }, + "operation": { + "type": "string", + "pattern": "^request$", + "required": true + }, + "mode": { + "type": "string", + "pattern": "^full$", + "required": true + }, + "payment": { + "type": "object", + "required": true, + "properties": { + "name": { + "type": "string", + "maxLength": 1000, + "required": true + }, + "email": { + "type": "string", + "format": "email", + "required": true + }, + "currency_code": { + "type": "string", + "minLength": 3, + "maxLength": 3, + "required": true + }, + "amount_total": { + "type": "number", + "required": true + }, + "merchant_payment_code": { + "type": "string", + "maxLength": 40 + }, + "payment_type_code": { + "type": "string", + "maxLength": 32 + }, + "birth_date": { + "type": "string", + "pattern": "^[0-9]{2}/[0-9]{2}/[0-9]{4}$" + }, + "customer_ip": { + "type": "string", + "oneOf": [ + { + "format": "ipv4" + }, + { + "format": "ipv6" + } + ] + }, + "zipcode": { + "type": "string" + }, + "address": { + "type": "string", + "maxLength": 100 + }, + "street_number": { + "type": "string", + "maxLength": 30 + }, + "street_complement": { + "type": "string", + "maxLength": 100 + }, + "city": { + "type": "string", + "maxLength": 80 + }, + "state": { + "type": "string" + }, + "country": { + "type": "string", + "minLength": 2, + "maxLength": 2, + "required": true + }, + "phone_number": { + "type": "string", + "minLength": 10, + "maxLength": 15, + "required": true + }, + "user_value_1": { + "type": "string", + "maxLength": 20 + }, + "user_value_2": { + "type": "string", + "maxLength": 20 + }, + "user_value_3": { + "type": "string", + "maxLength": 20 + }, + "user_value_4": { + "type": "string", + "maxLength": 20 + }, + "user_value_5": { + "type": "string", + "maxLength": 20 + } + } + } + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/requestSchema.json b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/requestSchema.json new file mode 100644 index 00000000..25d33de3 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/Schemas/requestSchema.json @@ -0,0 +1,89 @@ +{ + "type": "object", + "properties": { + "integration_key": { + "type": "string", + "maxLength": 100, + "required": true + }, + "country": { + "type": "string", + "length": 2, + "required": true + }, + "name": { + "type": "string", + "maxLength": 100, + "required": true + }, + "phone_number": { + "type": "string", + "maxLength": 15, + "required": true + }, + "sub_acc_name": { + "type": "string", + "maxLength": 32 + }, + "sub_acc_image_url": { + "type": "string", + "maxLength": 200 + }, + "email": { + "type": "string", + "maxLength": 100, + "required": true + }, + "currency_code": { + "type": "string", + "length": 3, + "required": true + }, + "amount": { + "type": "number", + "required": true + }, + "merchant_payment_code": { + "type": "string", + "maxLength": 40, + "required": true + }, + "order_number": { + "type": "string", + "maxLength": 40 + }, + "payment_type_code": { + "type": "string", + "maxLength": 32, + "required": true + }, + "instalments": { + "type": "string", + "maxLength": 5 + }, + "notification_url": { + "type": "string", + "maxLength": 2000 + }, + "user_value_1": { + "type": "string", + "maxLength": 20 + }, + "user_value_2": { + "type": "string", + "maxLength": 20 + }, + "user_value_3": { + "type": "string", + "maxLength": 20 + }, + "user_value_4": { + "type": "string", + "maxLength": 20 + }, + "user_value_5": { + "type": "string", + "maxLength": 20 + } + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/TefPaymentAdapterTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/TefPaymentAdapterTest.php new file mode 100644 index 00000000..67da7822 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Adapters/TefPaymentAdapterTest.php @@ -0,0 +1,46 @@ + 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->tef()->businessPerson()->build(); + + $adapter = new TefPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $validator = new JsonSchema\Validator; + $validator->validate($result, $this->getSchema(['paymentSchema', 'brazilPaymentSchema'])); + + $this->assertTrue($validator->isValid(), $this->getJsonMessage($validator)); + } + + public function testRequestAttributeNumber() + { + $config = new Config([ + 'sandboxIntegrationKey' => 'testIntegrationKey' + ]); + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->tef()->businessPerson()->build(); + + $adapter = new TefPaymentAdapter($payment, $config); + $result = $adapter->transform(); + + $numberOfKeys = count((array) $result); + $this->assertEquals(5, $numberOfKeys); + $this->assertObjectHasAttribute('integration_key', $result); + $this->assertObjectHasAttribute('operation', $result); + $this->assertObjectHasAttribute('metadata', $result); + $this->assertObjectHasAttribute('payment', $result); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/CancelPaymentTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/CancelPaymentTest.php new file mode 100644 index 00000000..7cfadcb9 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/CancelPaymentTest.php @@ -0,0 +1,44 @@ +config = new Config([ + 'sandboxIntegrationKey' => $env->read('SANDBOX_INTEGRATION_KEY', 'default_integration_key') + ]); + + $text = '{"payment":{"hash":"59386e0a5f258ffed3bee6fecc9150f916d1e19aa50ec68c","pin":"484913390","merchant_payment_code":"145000015-1496870409","order_number":null,"status":"CO","status_date":"2017-06-07 18:20:13","open_date":"2017-06-07 18:20:10","confirm_date":"2017-06-07 18:20:13","transfer_date":null,"amount_br":"265.00","amount_ext":"80.00","amount_iof":"1.00","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-10","instalments":"1","payment_type_code":"itau","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","refunds":[{"id":"21425","merchant_refund_code":null,"status":"RE","request_date":"2017-06-08 17:34:59","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"10.23","description":"description"},{"id":"21426","merchant_refund_code":null,"status":"RE","request_date":"2017-06-08 17:36:35","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"10.23","description":"description"}],"customer":{"document":"07834442902","email":"cezar+brbrbrbrbr@ebanx.com","name":"CEZAR LUIZ SAMPAIO","birth_date":"1978-03-29"}},"refund":{"id":"21426","merchant_refund_code":null,"status":"RE","request_date":"2017-06-08 17:36:35","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"10.23","description":"description"},"operation":"refund","status":"SUCCESS"}'; + $this->client = new ClientForTests(new EchoEngine(Client::SANDBOX_URL, $text)); + } + + public function testCancel() + { + $cancel = new CancelPaymentForTests($this->config, $this->client); + $result = $cancel->request('59386e0a5f258ffed3bee6fecc9150f916d1e19aa50ec68c'); + + $this->assertArrayHasKey('payment', $result); + } +} + +class CancelPaymentForTests extends CancelPayment +{ + public function __construct(Config $config, Client $client) + { + $this->client = $client; + parent::__construct($config); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/ExchangeTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/ExchangeTest.php new file mode 100644 index 00000000..eb1db7d5 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/ExchangeTest.php @@ -0,0 +1,81 @@ + [ + 'code' => 'USD', + 'base_code' => '???', + 'name' => 'US Dollar to Something', + 'rate' => self::TEST_RATE, + ], + 'status' => 'SUCCESS', + ]); + + $this->config = new Config([ + 'baseCurrency' => Currency::USD, + ]); + $this->client = new ClientForTests(new EchoEngine(Client::SANDBOX_URL, $text)); + } + + public function testSiteToLocalConvertion() + { + $subject = new Exchange($this->config, $this->client); + + $rate = $subject->siteToLocal(Currency::BRL); + $this->assertEquals(self::TEST_RATE, $rate); + } + + public function testLocalToSiteConvertion() + { + $subject = new Exchange($this->config, $this->client); + + $rate = $subject->localToSite(Currency::BRL); + $this->assertEquals(self::TEST_RATE, $rate); + } + + public function testSiteToSiteConvertion() + { + $subject = new Exchange($this->config, $this->client); + + $rate = $subject->localToSite(Currency::USD); + $this->assertEquals(1, $rate); + } + + public function testGlobalToGlobalConvertion() + { + $subject = new Exchange($this->config, $this->client); + + $rate = $subject->localToSite(Currency::EUR); + $this->assertEquals(0, $rate); + } + + public function testReturnZeroOnConvertionError() + { + $text = json_encode([ + "status" => "ERROR" + ]); + $client = new ClientForTests(new EchoEngine(Client::SANDBOX_URL, $text)); + $subject = new Exchange($this->config, $client); + + $rate = $subject->localToSite(Currency::BRL); + $this->assertEquals(0, $rate); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/BalotoTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/BalotoTest.php new file mode 100644 index 00000000..2bcbc37f --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/BalotoTest.php @@ -0,0 +1,101 @@ +getBalotoSuccessfulResponseJson(); + $client = $this->getMockedClient($balotoSuccessfulResponse); + + $factory = new BuilderFactory('es_CO'); + $payment = $factory->payment()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Baloto($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::COLOMBIA, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Baloto(new Config([ + 'baseCurrency' => Currency::COP, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::COLOMBIA, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Baloto(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/print/baloto/?hash=5925f3653b2c75e9ce7568d4f16c6d67648b8e92f7f05fa5', + $gateway->getUrl('5925f3653b2c75e9ce7568d4f16c6d67648b8e92f7f05fa5') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/print/baloto/?hash=5925f3653b2c75e9ce7568d4f16c6d67648b8e92f7f05fa5', + $gateway->getUrl('5925f3653b2c75e9ce7568d4f16c6d67648b8e92f7f05fa5', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4') + ); + } + + public function getBalotoSuccessfulResponseJson() + { + return '{"payment":{"hash":"5925f3653b2c75e9ce7568d4f16c6d67648b8e92f7f05fa5","pin":"484939887","merchant_payment_code":"27c251a65a854cfa74d052e66bdac8e8","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-24 17:56:05","confirm_date":null,"transfer_date":null,"amount_br":"152023.00","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"2355.1200","currency_ext":"USD","due_date":"2017-05-27","instalments":"1","payment_type_code":"baloto","baloto_url":"https:\/\/sandbox.ebanx.com\/print\/baloto\/?hash=5925f3653b2c75e9ce7568d4f16c6d67648b8e92f7f05fa5","voucher_id":"484939887","pre_approved":false,"capture_available":null,"note":"Fake payment created by PHPUnit.","customer":{"document":"932221309","email":"alfaro.mara@loya.es.co","name":"LUNA GRANADOS","birth_date":"1966-05-19"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return BalotoForTests + */ + private function getTestGateway($client = null) + { + $gateway = new Baloto($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/BoletoTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/BoletoTest.php new file mode 100644 index 00000000..487ffacc --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/BoletoTest.php @@ -0,0 +1,116 @@ +getBoletoSuccessfulResponseJson(); + $client = $this->getMockedClient($boletoSuccessfulResponse); + + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->boleto()->businessPerson()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->create($payment); + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testBusinessPersonRequest() + { + $boletoSuccessfulResponse = $this->getBoletoSuccessfulResponseJson(); + $client = $this->getMockedClient($boletoSuccessfulResponse); + + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->boleto()->businessPerson()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->request($payment); + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Boleto($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::BRAZIL, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Boleto(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::BRAZIL, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Boleto(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/print/?hash=591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4', + $gateway->getUrl('591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/print/?hash=591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4', + $gateway->getUrl('591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4') + ); + } + + public function getBoletoSuccessfulResponseJson() + { + return '{"payment":{"hash":"591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4","pin":"670071563","merchant_payment_code":"248b2672f000e293268be28d6048d600","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-16 19:42:05","confirm_date":null,"transfer_date":null,"amount_br":"48.81","amount_ext":"48.63","amount_iof":"0.18","currency_rate":"1.0000","currency_ext":"BRL","due_date":"2018-11-22","instalments":"1","payment_type_code":"boleto","boleto_url":"https:\/\/sandbox.ebanx.com\/print\/?hash=591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4","boleto_barcode":"34191760071244348372714245740007871600000004881","boleto_barcode_raw":"34198716000000048811760012443483721424574000","pre_approved":false,"capture_available":null,"customer":{"document":"40701766000118","email":"sdasneves@r7.com","name":"SR GUSTAVO FERNANDO VALENCIA","birth_date":"1978-03-28"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return BoletoForTests + */ + private function getTestGateway(Client $client = null) + { + $gateway = new Boleto($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/CreditCardTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/CreditCardTest.php new file mode 100644 index 00000000..5f618643 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/CreditCardTest.php @@ -0,0 +1,290 @@ +getCreditCardSuccessfulResponseJson(); + $client = $this->getMockedClient($creditCardSuccessfulResponse); + + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->creditCard()->businessPerson()->build(); + $gateway = new CreditCard($this->config, $creditCardConfig, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testCaptureByHash() + { + $response = '{"payment":{"hash":"593edc391aca7d44c51928295946d95b24360f4afa61fb1d","pin":"887838438","merchant_payment_code":"43-b08597ff128f43a3335abf24ff3b5d08","order_number":"43","status":"CO","status_date":"2017-06-12 15:25:29","open_date":"2017-06-12 15:23:53","confirm_date":"2017-06-12 15:25:29","transfer_date":null,"amount_br":"301.14","amount_ext":"300.00","amount_iof":"1.14","currency_rate":"1.0000","currency_ext":"BRL","due_date":"2017-06-12","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"OK","description":"Sandbox - Test credit card, transaction captured"},"pre_approved":true,"capture_available":false,"user_value_1":"from_woocommerce","user_value_3":"version=1.13.0","customer":{"document":"35433160874","email":"guilherme.pressutto+br@ebanx.com","name":"GUILHERME PRESSUTTO","birth_date":"1995-12-13"}},"status":"SUCCESS"}'; + $client = $this->getMockedClient($response); + + $creditCardConfig = new CreditCardConfig(); + $gateway = new CreditCard($this->config, $creditCardConfig, $client); + + $result = $gateway->captureByHash('593edc391aca7d44c51928295946d95b24360f4afa61fb1d'); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testCaptureByMerchantPaymentCode() + { + $response = '{"payment":{"hash":"593edc391aca7d44c51928295946d95b24360f4afa61fb1d","pin":"887838438","merchant_payment_code":"43-b08597ff128f43a3335abf24ff3b5d08","order_number":"43","status":"CO","status_date":"2017-06-12 15:25:29","open_date":"2017-06-12 15:23:53","confirm_date":"2017-06-12 15:25:29","transfer_date":null,"amount_br":"301.14","amount_ext":"300.00","amount_iof":"1.14","currency_rate":"1.0000","currency_ext":"BRL","due_date":"2017-06-12","instalments":"1","payment_type_code":"visa","transaction_status":{"acquirer":"EBANX","code":"OK","description":"Sandbox - Test credit card, transaction captured"},"pre_approved":true,"capture_available":false,"user_value_1":"from_woocommerce","user_value_3":"version=1.13.0","customer":{"document":"35433160874","email":"guilherme.pressutto+br@ebanx.com","name":"GUILHERME PRESSUTTO","birth_date":"1995-12-13"}},"status":"SUCCESS"}'; + $client = $this->getMockedClient($response); + + $creditCardConfig = new CreditCardConfig(); + $gateway = new CreditCard($this->config, $creditCardConfig, $client); + + $result = $gateway->captureByMerchantPaymentCode('43-b08597ff128f43a3335abf24ff3b5d08'); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSDEUR() + { + $creditCardConfig = new CreditCardConfig(); + $gateway = new CreditCard($this->config, $creditCardConfig); + $expectedCountries = [ + Country::BRAZIL, + Country::MEXICO, + Country::COLOMBIA, + Country::ARGENTINA, + ]; + + $this->assertAvailableForCountries($gateway, $expectedCountries); + + $gateway = new CreditCard(new Config([ + 'baseCurrency' => Currency::EUR, + ]), $creditCardConfig); + + $this->assertAvailableForCountries($gateway, $expectedCountries); + } + + public function testAvailabilityWithLocalCurrency() + { + $creditCardConfig = new CreditCardConfig(); + + $gateway = new CreditCard(new Config([ + 'baseCurrency' => Currency::BRL, + ]), $creditCardConfig); + + $this->assertAvailableForCountries($gateway, [ + Country::BRAZIL, + ]); + + $gateway = new CreditCard(new Config([ + 'baseCurrency' => Currency::MXN, + ]), $creditCardConfig); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + + $gateway = new CreditCard(new Config([ + 'baseCurrency' => Currency::COP, + ]), $creditCardConfig); + + $this->assertAvailableForCountries($gateway, [ + Country::COLOMBIA, + ]); + + $gateway = new CreditCard(new Config([ + 'baseCurrency' => Currency::ARS, + ]), $creditCardConfig); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $creditCardConfig = new CreditCardConfig(); + + $gateway = new CreditCard(new Config([ + 'baseCurrency' => Currency::CLP, + ]), $creditCardConfig); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testGatewayDefaultMinimumInstalment() + { + $usdToBrlRate = 3.4743; + $client = $this->getMockedClient($this->getExchangeRateSuccessfulResponseJsonWithRate($usdToBrlRate)); + + $gateway = new CreditCard($this->config, new CreditCardConfig(), $client); + + $defaultMinInstalment = CreditCardConfig::acquirerMinInstalmentValueForCurrency(Currency::MXN); + $country = Country::MEXICO; + $minInstalment = $gateway->getMinInstalmentValueForCountry($country); + + $this->assertEquals($defaultMinInstalment, $minInstalment); + + $gateway = new CreditCard($this->config, new CreditCardConfig([ + 'minInstalmentAmount' => $defaultMinInstalment * 1.2 + ]), $client); + + $minInstalment = $gateway->getMinInstalmentValueForCountry($country); + + $this->assertNotEquals($defaultMinInstalment, $minInstalment); + } + + public function testPaymentTermsForCountryAndValue() + { + $usdToBrlRate = 3.4743; + $config = new Config([ + 'baseCurrency' => Currency::USD, + ]); + + $creditCardConfig = new CreditCardConfig(); + for ($i = 4; $i <= 6; $i++) { + $creditCardConfig->addInterest($i, 5); + } + for ($i = 7; $i <= 12; $i++) { + $creditCardConfig->addInterest($i, 10); + } + + $gateway = $this->setupGateway($usdToBrlRate, $config, $creditCardConfig); + $country = Country::BRAZIL; + + $value = 12.75; + // 12.75 (USD) * 3.4743 (Exchange Rate) / 5 (BRL minimum instalment value) * 10% (max interest rate) = 9 instalments + + $paymentTerms = $gateway->getPaymentTermsForCountryAndValue($country, $value); + + $this->assertTrue(is_array($paymentTerms), 'Failed to return array of payment terms'); + $this->assertEquals( + 9, + count($paymentTerms), + 'Wrong number of payment terms' + ); + + $interest = 0; + for ($i = 0; $i < 3; $i++) { + $this->assertInterestInPaymentTerm($paymentTerms[$i], $value, $interest); + } + + $interest = 0.05; + for ($i = 3; $i < 6; $i++) { + $this->assertInterestInPaymentTerm($paymentTerms[$i], $value, $interest); + } + + $interest = 0.1; + for ($i = 6; $i < 9; $i++) { + $this->assertInterestInPaymentTerm($paymentTerms[$i], $value, $interest); + } + } + + public function testPaymentTermsMerchantTaxFlagOn() + { + $usdToBrlRate = 3.4743; + $config = new Config([ + 'baseCurrency' => Currency::USD, + 'taxesOnMerchant' => true, + ]); + + $gateway = $this->setupGateway($usdToBrlRate, $config); + $country = Country::BRAZIL; + + $value = rand(100, 9999) / 100; + $localAmountWithoutTax = $value * $usdToBrlRate; + + $paymentTerms = $gateway->getPaymentTermsForCountryAndValue($country, $value); + $this->assertEquals( + $localAmountWithoutTax, + $paymentTerms[0]->localAmountWithTax, + 'Local amount should have no taxes' + ); + } + + public function testPaymentTermsBelowMinimumAmount() + { + $country = Country::BRAZIL; + $value = CreditCardConfig::acquirerMinInstalmentValueForCurrency(Currency::localForCountry($country)) - 1; + $gateway = $this->setupGateway(1, new Config()); + + $paymentTerms = $gateway->getPaymentTermsForCountryAndValue($country, $value); + $this->assertNotNull( + $paymentTerms[0], + 'On spot payment should be allowed' + ); + } + + public function testPaymentTermsMerchantTaxFlagOff() + { + $usdToBrlRate = 3.4743; + $config = new Config([ + 'baseCurrency' => Currency::USD, + 'taxesOnMerchant' => false, + ]); + + $gateway = $this->setupGateway($usdToBrlRate, $config); + $country = Country::BRAZIL; + + $value = 50.0; + $localAmountWithTax = $value * $usdToBrlRate * (1 + Config::IOF); + + $paymentTerms = $gateway->getPaymentTermsForCountryAndValue($country, $value); + $this->assertEquals( + $localAmountWithTax, + $paymentTerms[0]->localAmountWithTax, + 'Local amount should have taxes' + ); + } + + private function setupGateway($usdToBrlRate, $config, $creditCardConfig = null) + { + $client = $this->getMockedClient($this->getExchangeRateSuccessfulResponseJsonWithRate($usdToBrlRate)); + + if (!$creditCardConfig) { + $creditCardConfig = new CreditCardConfig(); + } + return new CreditCard($config, $creditCardConfig, $client); + } + + private function getCreditCardSuccessfulResponseJson() + { + return '{"payment":{"hash":"591f7a1cae81aaaade3f76014310da4a7289ab651e6ec44e","pin":"440297024","merchant_payment_code":"c1ef11f4be81d3515d2879d486718508","order_number":null,"status":"CA","status_date":"2017-05-19 20:05:00","open_date":"2017-05-19 20:04:59","confirm_date":null,"transfer_date":null,"amount_br":"48.81","amount_ext":"48.63","amount_iof":"0.18","currency_rate":"1.0000","currency_ext":"BRL","due_date":"2017-05-22","instalments":"1","payment_type_code":"mastercard","transaction_status":{"acquirer":"EBANX","code":"NOK","description":"Sandbox - Not a test card, transaction declined"},"pre_approved":false,"capture_available":false,"note":"Fake payment created by PHPUnit.","customer":{"document":"60639321000162","email":"ksalgado@furtado.org","name":"DR FRANCO MASCARENHAS SOBRINHO","birth_date":"1971-01-07"}},"status":"SUCCESS"}'; + } + + /** + * @param PaymentTerm $paymentTerm + * @param float $originalValue + * @param float $interestRate + */ + private function assertInterestInPaymentTerm(PaymentTerm $paymentTerm, $originalValue, $interestRate) + { + $hasInterestFailMessage = 'Failed to mark term ' . $paymentTerm->instalmentNumber . ' with interest flag accordingly'; + $interestCalcFailMessage = 'Failed to add interest to term ' . $paymentTerm->instalmentNumber; + + $ratio = 1 + $interestRate; + $total = $paymentTerm->instalmentNumber * $paymentTerm->baseAmount; + $crossCheck = $total / $ratio; + + $this->assertEquals($interestRate !== 0, $paymentTerm->hasInterests, $hasInterestFailMessage); + $this->assertEquals($originalValue, $crossCheck, $interestCalcFailMessage); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/DebitCardTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/DebitCardTest.php new file mode 100644 index 00000000..8534bed8 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/DebitCardTest.php @@ -0,0 +1,72 @@ +getDebitCardSuccessfulResponseJson(); + $client = $this->getMockedClient($creditCardSuccessfulResponse); + + $factory = new BuilderFactory('es_MX'); + $payment = $factory->payment()->debitCard()->build(); + $gateway = new DebitCard($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSDEUR() + { + $gateway = new DebitCard($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + + $gateway = new DebitCard(new Config([ + 'baseCurrency' => Currency::EUR, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new DebitCard(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new DebitCard(new Config([ + 'baseCurrency' => Currency::CLP, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + private function getDebitCardSuccessfulResponseJson() + { + return '{"payment":{"hash":"592c955dbc4e2d9afcf3f0ba558b3d87e209d49ca57597ea","pin":"403031613","merchant_payment_code":"6a9b9bba03c0ab16c4ee8fb14473d1fe","order_number":null,"status":"CO","status_date":"2017-05-29 18:40:44","open_date":"2017-05-29 18:40:44","confirm_date":"2017-05-29 18:40:44","transfer_date":null,"amount_br":"1362.01","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"21.1000","currency_ext":"USD","due_date":"2017-06-01","instalments":"1","payment_type_code":"debitcard","transaction_status":{"acquirer":"EBANX","code":"OK","description":"Sandbox - Test debit card, transaction captured"},"pre_approved":true,"capture_available":false,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"932221309","email":"alfaro.mara@loya.es.mx","name":"LUNA GRANADOS","birth_date":"1966-05-24"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/DirectGatewayTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/DirectGatewayTest.php new file mode 100644 index 00000000..7b652c57 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/DirectGatewayTest.php @@ -0,0 +1,80 @@ + false]); + $gateway = new TestGateway($config); + + $this->assertEquals(Client::MODE_LIVE, $gateway->getClient()->getMode()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowWithInvalidCountry() + { + $gateway = new TestGateway(new Config()); + $gateway->countryNotAvailable(); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testEnabledCountriesGetterNotOverridden() + { + NoCountryNoCurrencyGateway::acceptsCountry('test'); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testEnabledCurrenciesGetterNotOverridden() + { + NoCountryNoCurrencyGateway::acceptsCurrency('test'); + } +} + +class NoCountryNoCurrencyGateway extends DirectGateway +{ + protected function getPaymentData(Payment $payment) + { + return; + } +} + +class TestGateway extends DirectGateway +{ + public function getClient() + { + return $this->client; + } + + public function countryNotAvailable() + { + $this->availableForCountryOrThrow('invalidCountry'); + } + + protected function getPaymentData(Payment $payment) + { + return; + } + + protected static function getEnabledCountries() + { + return []; + } + + protected static function getEnabledCurrencies() + { + return []; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/EbanxAccountTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/EbanxAccountTest.php new file mode 100644 index 00000000..5ba44542 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/EbanxAccountTest.php @@ -0,0 +1,72 @@ +getEbanxAccountSuccessfulResponseJson(); + $client = $this->getMockedClient($ebanxAccountSuccessfulResponse); + + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->build(); + $gateway = new EbanxAccount($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new EbanxAccount($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::BRAZIL, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new EbanxAccount(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::BRAZIL, + ]); + } + + public function testAvailabilityWithEUR() + { + $gateway = new EbanxAccount(new Config([ + 'baseCurrency' => Currency::EUR, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new EbanxAccount(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getEbanxAccountSuccessfulResponseJson() + { + return '{"redirect_url":"https:\/\/sandbox.ebanx.com\/ws\/directtefredirect\/?hash=5928c60eaa72a35e9f6518d84d35fb270e0a79718fc1feb9","payment":{"hash":"5928c60eaa72a35e9f6518d84d35fb270e0a79718fc1feb9","pin":"214657893","merchant_payment_code":"145bcbe3ac952833ff122fb50e9f00d1","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-26 21:19:26","confirm_date":null,"transfer_date":null,"amount_br":"48.81","amount_ext":"48.63","amount_iof":"0.18","currency_rate":"1.0000","currency_ext":"USD","due_date":"2017-05-29","instalments":"1","payment_type_code":"ebanxaccount","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"04540945076","email":"luana79@serra.net.br","name":"SR THIAGO VIEIRA NETO","birth_date":"1955-01-25"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/EftTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/EftTest.php new file mode 100644 index 00000000..2934df35 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/EftTest.php @@ -0,0 +1,63 @@ +getEftSuccessfulResponseJson(); + $client = $this->getMockedClient($eftSuccessfulResponse); + + $factory = new BuilderFactory('es_CO'); + $payment = $factory->payment()->eft()->build(); + $gateway = new Eft($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Eft($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::COLOMBIA, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Eft(new Config([ + 'baseCurrency' => Currency::COP, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::COLOMBIA, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Eft(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getEftSuccessfulResponseJson() + { + return '{"redirect_url":"https:\/\/sandbox.ebanx.com\/ws\/simulator\/confirm?hash=592c75608acb71e6f460627e5b8a3b0a9cbe98252139ecf6","payment":{"hash":"592c75608acb71e6f460627e5b8a3b0a9cbe98252139ecf6","pin":"697372181","merchant_payment_code":"897cb9bdc94c45c7ccf6198751b23d7e","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-29 16:24:16","confirm_date":null,"transfer_date":null,"amount_br":"152023.00","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"2355.1200","currency_ext":"USD","due_date":"2017-06-01","instalments":"1","payment_type_code":"eft","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.co","name":"LUNA GRANADOS","birth_date":"1966-05-24"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/GatewayTestCase.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/GatewayTestCase.php new file mode 100644 index 00000000..eb14ad76 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/GatewayTestCase.php @@ -0,0 +1,52 @@ +config = new Config([ + 'sandboxIntegrationKey' => $env->read('SANDBOX_INTEGRATION_KEY', 'default_integration_key'), + ]); + } + + protected function getExchangeRateSuccessfulResponseJsonWithRate($rate) + { + return '{"currency_rate":{"code":"USD","base_code":"???","name":"US Dollar to Something","rate":"'.$rate.'"},"status":"SUCCESS"}'; + } + + protected function assertAvailableForCountries(BaseGateway $gateway, $countries) + { + $allCountries = Country::all(); + + foreach ($allCountries as $country) { + if (in_array($country, $countries)) { + $this->assertTrue($gateway->isAvailableForCountry($country)); + continue; + } + + $this->assertFalse($gateway->isAvailableForCountry($country)); + } + } + + protected function assertNotAvailableAnywhere(BaseGateway $gateway) + { + $this->assertFalse($gateway->isAvailableForCountry(Country::BRAZIL)); + $this->assertFalse($gateway->isAvailableForCountry(Country::CHILE)); + $this->assertFalse($gateway->isAvailableForCountry(Country::MEXICO)); + $this->assertFalse($gateway->isAvailableForCountry(Country::PERU)); + $this->assertFalse($gateway->isAvailableForCountry(Country::COLOMBIA)); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/HostedTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/HostedTest.php new file mode 100644 index 00000000..78458ddb --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/HostedTest.php @@ -0,0 +1,51 @@ +getHostedSuccessfulResponseJson(); + $client = $this->getMockedClient($hostedSuccessfulResponse); + + $factory = new BuilderFactory('pt_BR'); + $request = $factory->request()->build(); + $gateway = new Hosted($this->config, $client); + + $result = $gateway->create($request); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Hosted($this->config); + + $this->assertAvailableForCountries($gateway, Country::all()); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Hosted(new Config([ + 'baseCurrency' => Currency::ARS, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function getHostedSuccessfulResponseJson() + { + return '{"payment":{"hash":"'.md5(rand(1, 999)).'"},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/MulticajaTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/MulticajaTest.php new file mode 100644 index 00000000..0588ad6b --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/MulticajaTest.php @@ -0,0 +1,63 @@ +getMulticajaSuccessfulResponseJson(); + $client = $this->getMockedClient($multicajaSuccessfulResponse); + + $factory = new BuilderFactory('es_CL'); + $payment = $factory->payment()->build(); + $gateway = new Multicaja($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Multicaja($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Multicaja(new Config([ + 'baseCurrency' => Currency::CLP + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Multicaja(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getMulticajaSuccessfulResponseJson() + { + return '{"redirect_url":"https://api.ebanx.com/flowcl/redirect/execute?hash=599b0e10f0772e384a39edf8dc90d4d1d9efc6635abf8dd9&flow_payment_method=multicaja","payment":{"hash":"59260b09b9d3b15e2c5b42b42f3752ce2225d6f27f686236","pin":"020330756","merchant_payment_code":"90bf954f559b30eb710fb0f49df23f2d","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-24 19:36:56","confirm_date":null,"transfer_date":null,"amount_br":"44933.00","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"696.0900","currency_ext":"USD","due_date":"2017-05-27","instalments":"1","payment_type_code":"flowcl","pre_approved":false,"capture_available":null,"note":"Fake payment created by PHPUnit.","customer":{"document":"932221309","email":"alfaro.mara@loya.es.cl","name":"LUNA GRANADOS","birth_date":"1966-05-19"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/OtrosCuponesTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/OtrosCuponesTest.php new file mode 100644 index 00000000..5b406db6 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/OtrosCuponesTest.php @@ -0,0 +1,101 @@ +getOtrosCuponesSuccessfulResponseJson(); + $client = $this->getMockedClient($cuponSuccessfulResponse); + + $factory = new BuilderFactory('es_AR'); + $payment = $factory->payment()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new OtrosCupones($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new OtrosCupones(new Config([ + 'baseCurrency' => Currency::ARS, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new OtrosCupones(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/print/voucher/?hash=59dd49e565e80d37b1995a9dfa2767e2494060237b13c3b8', + $gateway->getUrl('59dd49e565e80d37b1995a9dfa2767e2494060237b13c3b8') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/print/voucher/?hash=59dd49e565e80d37b1995a9dfa2767e2494060237b13c3b8', + $gateway->getUrl('59dd49e565e80d37b1995a9dfa2767e2494060237b13c3b8', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('59dd49e565e80d37b1995a9dfa2767e2494060237b13c3b8') + ); + } + + public function getOtrosCuponesSuccessfulResponseJson() + { + return '{"redirect_url":null,"payment":{"hash":"59dd49e565e80d37b1995a9dfa2767e2494060237b13c3b8","pin":"235141564","merchant_payment_code":"1733c732892e95a806002a4147f3f1ee","order_number":null,"status":"PE","status_date":null,"open_date":"2017-10-10 22:29:57","confirm_date":null,"transfer_date":null,"amount_br":"196.99","amount_ext":"52.39","amount_iof":"0.00","currency_rate":"3.7600","currency_ext":"USD","due_date":"2017-10-13","instalments":"1","payment_type_code":"cupon","voucher_url":"https:\/\/sandbox.ebanx.com\/print\/voucher\/execute?hash=59dd49e565e80d37b1995a9dfa2767e2494060237b13c3b8","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.ar","name":"LUNA GRANADOS","birth_date":"1966-10-05"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return OtrosCuponesForTests + */ + private function getTestGateway($client = null) + { + $gateway = new OtrosCupones($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/OxxoTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/OxxoTest.php new file mode 100644 index 00000000..f473e947 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/OxxoTest.php @@ -0,0 +1,101 @@ +getOxxoSuccessfulResponseJson(); + $client = $this->getMockedClient($oxxoSuccessfulResponse); + + $factory = new BuilderFactory('es_MX'); + $payment = $factory->payment()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Oxxo($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Oxxo(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Oxxo(new Config([ + 'baseCurrency' => Currency::CLP, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/print/oxxo/?hash=5924c698895ed4cf3764c681fe9496f8fe0a986b070a594e', + $gateway->getUrl('5924c698895ed4cf3764c681fe9496f8fe0a986b070a594e') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/print/oxxo/?hash=5924c698895ed4cf3764c681fe9496f8fe0a986b070a594e', + $gateway->getUrl('5924c698895ed4cf3764c681fe9496f8fe0a986b070a594e', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4') + ); + } + + public function getOxxoSuccessfulResponseJson() + { + return '{"payment":{"hash":"5924c698895ed4cf3764c681fe9496f8fe0a986b070a594e","pin":"484657684","merchant_payment_code":"c15a03ecdea06d3c55db001af76c6186","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-23 20:32:40","confirm_date":null,"transfer_date":null,"amount_br":"1362.01","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"21.1000","currency_ext":"USD","due_date":"2017-05-26","instalments":"1","payment_type_code":"oxxo","oxxo_url":"https:\/\/sandbox.ebanx.com\/print\/oxxo\/?hash=5924c698895ed4cf3764c681fe9496f8fe0a986b070a594e","oxxo_barcode":"51000000000020022017052601362015","pre_approved":false,"capture_available":null,"note":"Fake payment created by PHPUnit.","customer":{"document":"932221309","email":"alfaro.mara@loya.es","name":"LUNA GRANADOS","birth_date":"1966-05-18"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return OxxoForTests + */ + private function getTestGateway(Client $client = null) + { + $gateway = new Oxxo($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/PagoEfectivoTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/PagoEfectivoTest.php new file mode 100644 index 00000000..a7454164 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/PagoEfectivoTest.php @@ -0,0 +1,101 @@ +getPagoEfectivoSuccessfulResponseJson(); + $client = $this->getMockedClient($pagoEfectivoSuccessfulResponse); + + $factory = new BuilderFactory('es_PE'); + $payment = $factory->payment()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new PagoEfectivo($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::PERU, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new PagoEfectivo(new Config([ + 'baseCurrency' => Currency::PEN, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::PERU, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new PagoEfectivo(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/cip/?hash=59260f14654c5e69344fec4cd0e22fe1d9177a5898e3a3d3', + $gateway->getUrl('59260f14654c5e69344fec4cd0e22fe1d9177a5898e3a3d3') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/cip/?hash=59260f14654c5e69344fec4cd0e22fe1d9177a5898e3a3d3', + $gateway->getUrl('59260f14654c5e69344fec4cd0e22fe1d9177a5898e3a3d3', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('591b803da5549b6a1bac524b31e6eef55c2e67af8e40e1e4') + ); + } + + public function getPagoEfectivoSuccessfulResponseJson() + { + return '{"payment":{"hash":"59260f14654c5e69344fec4cd0e22fe1d9177a5898e3a3d3","pin":"841280229","merchant_payment_code":"5e37ce7c2f03c2dc1e3599e869ddc633","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-24 19:54:11","confirm_date":null,"transfer_date":null,"amount_br":"566.08","amount_ext":"113.21","amount_iof":"0.03","currency_rate":"5.0000","currency_ext":"USD","due_date":"2017-05-23","instalments":"1","payment_type_code":"pagoefectivo","cip_url":"https:\/\/sandbox.ebanx.com\/cip\/?hash=59260f14654c5e69344fec4cd0e22fe1d9177a5898e3a3d3","cip_code":"2829662","pre_approved":false,"capture_available":null,"note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.pe","name":"LUNA GRANADOS","birth_date":"1966-05-19"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return PagoEfectivoForTests + */ + private function getTestGateway(Client $client = null) + { + $gateway = new PagoEfectivo($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/PagofacilTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/PagofacilTest.php new file mode 100644 index 00000000..fe82b342 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/PagofacilTest.php @@ -0,0 +1,101 @@ +getPagofacilSuccessfulResponseJson(); + $client = $this->getMockedClient($pagofacilSuccessfulResponse); + + $factory = new BuilderFactory('es_AR'); + $payment = $factory->payment()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Pagofacil($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Pagofacil(new Config([ + 'baseCurrency' => Currency::ARS, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Pagofacil(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/print/voucher/?hash=59dd4830ba89b516ba301a73a18c0f5cc09d58660a888575', + $gateway->getUrl('59dd4830ba89b516ba301a73a18c0f5cc09d58660a888575') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/print/voucher/?hash=59dd4830ba89b516ba301a73a18c0f5cc09d58660a888575', + $gateway->getUrl('59dd4830ba89b516ba301a73a18c0f5cc09d58660a888575', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('59dd4830ba89b516ba301a73a18c0f5cc09d58660a888575') + ); + } + + public function getPagofacilSuccessfulResponseJson() + { + return '{"redirect_url":null,"payment":{"hash":"59dd4830ba89b516ba301a73a18c0f5cc09d58660a888575","pin":"617869683","merchant_payment_code":"825c2c20353d1ec16c422a4ee9f6415f","order_number":null,"status":"PE","status_date":null,"open_date":"2017-10-10 22:22:39","confirm_date":null,"transfer_date":null,"amount_br":"196.99","amount_ext":"52.39","amount_iof":"0.00","currency_rate":"3.7600","currency_ext":"USD","due_date":"2017-10-13","instalments":"1","payment_type_code":"pagofacil","voucher_url":"https:\/\/sandbox.ebanx.com\/print\/voucher\/execute?hash=59dd4830ba89b516ba301a73a18c0f5cc09d58660a888575","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.ar","name":"LUNA GRANADOS","birth_date":"1966-10-05"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return PagofacilForTests + */ + private function getTestGateway($client = null) + { + $gateway = new Pagofacil($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/RapipagoTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/RapipagoTest.php new file mode 100644 index 00000000..63ec2cf2 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/RapipagoTest.php @@ -0,0 +1,101 @@ +getRapipagoSuccessfulResponseJson(); + $client = $this->getMockedClient($rapipagoSuccessfulResponse); + + $factory = new BuilderFactory('es_AR'); + $payment = $factory->payment()->build(); + $gateway = $this->getTestGateway($client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Rapipago($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Rapipago(new Config([ + 'baseCurrency' => Currency::ARS, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::ARGENTINA, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Rapipago(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/print/voucher/?hash=59dd440f947b5097c8f6985c6a5cb71f935d80f745f37d5e', + $gateway->getUrl('59dd440f947b5097c8f6985c6a5cb71f935d80f745f37d5e') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/print/voucher/?hash=59dd440f947b5097c8f6985c6a5cb71f935d80f745f37d5e', + $gateway->getUrl('59dd440f947b5097c8f6985c6a5cb71f935d80f745f37d5e', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('59dd440f947b5097c8f6985c6a5cb71f935d80f745f37d5e') + ); + } + + public function getRapipagoSuccessfulResponseJson() + { + return '{"redirect_url":null,"payment":{"hash":"59dd440f947b5097c8f6985c6a5cb71f935d80f745f37d5e","pin":"841604151","merchant_payment_code":"d5ce2d8b700f263e3edae156330b44a1","order_number":null,"status":"PE","status_date":null,"open_date":"2017-10-10 22:05:02","confirm_date":null,"transfer_date":null,"amount_br":"196.99","amount_ext":"52.39","amount_iof":"0.00","currency_rate":"3.7600","currency_ext":"USD","due_date":"2017-10-13","instalments":"1","payment_type_code":"rapipago","voucher_url":"https:\/\/sandbox.ebanx.com\/print\/voucher\/execute?hash=59dd440f947b5097c8f6985c6a5cb71f935d80f745f37d5e","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.ar","name":"LUNA GRANADOS","birth_date":"1966-10-05"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return RapipagoForTests + */ + private function getTestGateway($client = null) + { + $gateway = new Rapipago($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SafetyPayCashTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SafetyPayCashTest.php new file mode 100644 index 00000000..99cc751a --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SafetyPayCashTest.php @@ -0,0 +1,70 @@ +getSafetyPayCashSuccessfulResponseJson(); + $client = $this->getMockedClient($safetyPayCashSuccessfulResponse); + + $factory = new BuilderFactory('es_PE'); + $payment = $factory->payment()->build(); + $gateway = new SafetyPayCash($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new SafetyPayCash($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::ECUADOR, + Country::PERU, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new SafetyPayCash(new Config([ + 'baseCurrency' => Currency::PEN, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::PERU, + ]); + + $gateway = new SafetyPayCash(new Config([ + 'baseCurrency' => Currency::USD, + ])); + + $this->assertTrue($gateway->isAvailableForCountry(Country::ECUADOR)); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new SafetyPayCash(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getSafetyPayCashSuccessfulResponseJson() + { + return '{"redirect_url":"https:\/\/sandbox.ebanx.com\/ws\/simulator\/confirm?hash=592dd4785dd9aaac6868c065581c18a61ed002d73c8bf09b","payment":{"hash":"592dd4785dd9aaac6868c065581c18a61ed002d73c8bf09b","pin":"064849205","merchant_payment_code":"6f40f22e67db8ba64f13d621565c1fe8","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-30 17:22:16","confirm_date":null,"transfer_date":null,"amount_br":"566.08","amount_ext":"113.21","amount_iof":"0.03","currency_rate":"5.0000","currency_ext":"USD","due_date":"2017-06-02","instalments":"1","payment_type_code":"safetypay-cash","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.pe","name":"LUNA GRANADOS","birth_date":"1966-05-25"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SafetyPayOnlineTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SafetyPayOnlineTest.php new file mode 100644 index 00000000..8ee58590 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SafetyPayOnlineTest.php @@ -0,0 +1,70 @@ +getSafetyPayOnlineSuccessfulResponseJson(); + $client = $this->getMockedClient($safetyPayOnlineSuccessfulResponse); + + $factory = new BuilderFactory('es_PE'); + $payment = $factory->payment()->build(); + $gateway = new SafetyPayOnline($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new SafetyPayOnline($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::ECUADOR, + Country::PERU, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new SafetyPayOnline(new Config([ + 'baseCurrency' => Currency::PEN, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::PERU, + ]); + + $gateway = new SafetyPayOnline(new Config([ + 'baseCurrency' => Currency::USD, + ])); + + $this->assertTrue($gateway->isAvailableForCountry(Country::ECUADOR)); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new SafetyPayOnline(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getSafetyPayOnlineSuccessfulResponseJson() + { + return '{"redirect_url":"https:\/\/sandbox.ebanx.com\/ws\/simulator\/confirm?hash=592efc0d3532803c93dce20cfe8850ada40ae1b0cafca009","payment":{"hash":"592efc0d3532803c93dce20cfe8850ada40ae1b0cafca009","pin":"429011940","merchant_payment_code":"3fbf7553619150657708fca8e4bc217d","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-31 14:23:24","confirm_date":null,"transfer_date":null,"amount_br":"261.96","amount_ext":"52.39","amount_iof":"0.01","currency_rate":"5.0000","currency_ext":"USD","due_date":"2017-06-03","instalments":"1","payment_type_code":"safetypay-online","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.pe","name":"LUNA GRANADOS","birth_date":"1966-05-26"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SencillitoTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SencillitoTest.php new file mode 100644 index 00000000..0498dfe6 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SencillitoTest.php @@ -0,0 +1,63 @@ +getSencillitoSuccessfulResponseJson(); + $client = $this->getMockedClient($sencillitoSuccessfulResponse); + + $factory = new BuilderFactory('es_CL'); + $payment = $factory->payment()->build(); + $gateway = new Sencillito($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Sencillito($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Sencillito(new Config([ + 'baseCurrency' => Currency::CLP, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Sencillito(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getSencillitoSuccessfulResponseJson() + { + return '{"redirect_url":"https:\/\/sandbox.ebanx.com\/ws\/simulator\/confirm?hash=59260b09b9d3b15e2c5b42b42f3752ce2225d6f27f686236","payment":{"hash":"59260b09b9d3b15e2c5b42b42f3752ce2225d6f27f686236","pin":"020330756","merchant_payment_code":"90bf954f559b30eb710fb0f49df23f2d","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-24 19:36:56","confirm_date":null,"transfer_date":null,"amount_br":"44933.00","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"696.0900","currency_ext":"USD","due_date":"2017-05-27","instalments":"1","payment_type_code":"sencillito","pre_approved":false,"capture_available":null,"note":"Fake payment created by PHPUnit.","customer":{"document":"932221309","email":"alfaro.mara@loya.es.cl","name":"LUNA GRANADOS","birth_date":"1966-05-19"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/ServipagTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/ServipagTest.php new file mode 100644 index 00000000..7f059e50 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/ServipagTest.php @@ -0,0 +1,63 @@ +getServipagSuccessfulResponseJson(); + $client = $this->getMockedClient($servipagSuccessfulResponse); + + $factory = new BuilderFactory('es_CL'); + $payment = $factory->payment()->build(); + $gateway = new Servipag($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Servipag($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Servipag(new Config([ + 'baseCurrency' => Currency::CLP, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Servipag(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getServipagSuccessfulResponseJson() + { + return '{"redirect_url":"https:\/\/sandbox.ebanx.com\/ws\/directtefredirect\/?hash=592c83d8e658046969a23c1056da1a54276afff4f3cd2cb3","payment":{"hash":"592c83d8e658046969a23c1056da1a54276afff4f3cd2cb3","pin":"296386782","merchant_payment_code":"b784ee12a5230a3c9d070087267e6891","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-29 17:26:00","confirm_date":null,"transfer_date":null,"amount_br":"44933.00","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"696.0900","currency_ext":"USD","due_date":"2017-06-01","instalments":"1","payment_type_code":"servipag","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.cl","name":"LUNA GRANADOS","birth_date":"1966-05-24"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SpeiTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SpeiTest.php new file mode 100644 index 00000000..779c7c17 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/SpeiTest.php @@ -0,0 +1,101 @@ +getSpeiSuccessfulResponseJson(); + $client = $this->getMockedClient($eftSuccessfulResponse); + + $factory = new BuilderFactory('es_MX'); + $payment = $factory->payment()->eft()->build(); + $gateway = new Spei($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Spei($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Spei(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::MEXICO, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Spei(new Config([ + 'baseCurrency' => Currency::COP, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function testSandboxTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://sandbox.ebanx.com/print/spei/execute?hash=5a53bc54679dbc1f3baf868e9d5d257ee7367fd5c47983fc', + $gateway->getUrl('5a53bc54679dbc1f3baf868e9d5d257ee7367fd5c47983fc') + ); + } + + public function testLiveTicketUrl() + { + $gateway = $this->getTestGateway(); + $this->assertEquals( + 'https://print.ebanx.com/print/spei/execute?hash=5a53bc54679dbc1f3baf868e9d5d257ee7367fd5c47983fc', + $gateway->getUrl('5a53bc54679dbc1f3baf868e9d5d257ee7367fd5c47983fc', false) + ); + } + + public function testTicketContent() + { + $gateway = $this->getTestGateway($this->getMockedClient('')); + // TODO: assert something better + $this->assertContains( + 'getTicketHtml('5a53bc54679dbc1f3baf868e9d5d257ee7367fd5c47983fc') + ); + } + + public function getSpeiSuccessfulResponseJson() + { + return '{"redirect_url":null,"payment":{"hash":"59dd3ebed3328435c46b5b1130ec2cf98e94af449d3b6a51","pin":"001540362","merchant_payment_code":"d4bfe053764337a326309a1230294db8","order_number":null,"status":"PE","status_date":null,"open_date":"2017-10-10 21:42:22","confirm_date":null,"transfer_date":null,"amount_br":"2388.73","amount_ext":"113.21","amount_iof":"0.00","currency_rate":"21.1000","currency_ext":"USD","due_date":"2017-10-13","instalments":"1","payment_type_code":"spei","clabe_account":"646181141900000339","clabe_reference":"4262291","spei_url":"https:\/\/sandbox.ebanx.com\/print\/spei\/execute?hash=59dd3ebed3328435c46b5b1130ec2cf98e94af449d3b6a51","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","note":"Fake payment created by PHPUnit.","customer":{"document":"0","email":"alfaro.mara@loya.es.mx","name":"LUNA GRANADOS","birth_date":"1966-10-05"}},"status":"SUCCESS"}'; + } + + /** + * @param Client $client + * @return Spei + */ + private function getTestGateway(Client $client = null) + { + $gateway = new Spei($this->config, $client); + return $gateway; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/TefTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/TefTest.php new file mode 100644 index 00000000..15685b15 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/TefTest.php @@ -0,0 +1,63 @@ +getTefSuccessfulResponseJson(); + $client = $this->getMockedClient($tefSuccessfulResponse); + + $factory = new BuilderFactory('pt_BR'); + $payment = $factory->payment()->tef()->businessPerson()->build(); + $gateway = new Tef($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Tef($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::BRAZIL, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Tef(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::BRAZIL, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Tef(new Config([ + 'baseCurrency' => Currency::MXN, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getTefSuccessfulResponseJson() + { + return '{"redirect_url":"https:\/\/sandbox.ebanx.com\/ws\/directtefredirect\/?hash=5928bf451e5a4f07c7b6e1e067d97bdb003b520423134f53","payment":{"hash":"5928bf451e5a4f07c7b6e1e067d97bdb003b520423134f53","pin":"866264009","merchant_payment_code":"611549d83648c36b0d60849f3cba5b05","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-26 20:50:29","confirm_date":null,"transfer_date":null,"amount_br":"48.81","amount_ext":"48.63","amount_iof":"0.18","currency_rate":"1.0000","currency_ext":"BRL","due_date":"2017-05-29","instalments":"1","payment_type_code":"bancodobrasil","pre_approved":false,"capture_available":null,"note":"Fake payment created by PHPUnit.","customer":{"document":"40701766000118","email":"tessalia69@r7.com","name":"SR GUSTAVO FERNANDO VALENCIA","birth_date":"1984-05-12"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/WebpayTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/WebpayTest.php new file mode 100644 index 00000000..a2e6c41e --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Gateways/WebpayTest.php @@ -0,0 +1,63 @@ +getWebpaySuccessfulResponseJson(); + $client = $this->getMockedClient($webpaySuccessfulResponse); + + $factory = new BuilderFactory('es_CL'); + $payment = $factory->payment()->build(); + $gateway = new Webpay($this->config, $client); + + $result = $gateway->create($payment); + + $this->assertArrayHasKey('payment', $result); + + // TODO: assert output (to be defined) + } + + public function testAvailabilityWithUSD() + { + $gateway = new Webpay($this->config); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithLocalCurrency() + { + $gateway = new Webpay(new Config([ + 'baseCurrency' => Currency::CLP, + ])); + + $this->assertAvailableForCountries($gateway, [ + Country::CHILE, + ]); + } + + public function testAvailabilityWithWrongLocalCurrency() + { + $gateway = new Webpay(new Config([ + 'baseCurrency' => Currency::BRL, + ])); + + $this->assertNotAvailableAnywhere($gateway); + } + + public function getWebpaySuccessfulResponseJson() + { + return '{"redirect_url":"https://api.ebanx.com/flowcl/redirect/execute?hash=599b0e10f0772e384a39edf8dc90d4d1d9efc6635abf8dd9&flow_payment_method=webpay","payment":{"hash":"59260b09b9d3b15e2c5b42b42f3752ce2225d6f27f686236","pin":"020330756","merchant_payment_code":"90bf954f559b30eb710fb0f49df23f2d","order_number":null,"status":"PE","status_date":null,"open_date":"2017-05-24 19:36:56","confirm_date":null,"transfer_date":null,"amount_br":"44933.00","amount_ext":"64.55","amount_iof":"0.00","currency_rate":"696.0900","currency_ext":"USD","due_date":"2017-05-27","instalments":"1","payment_type_code":"flowcl","pre_approved":false,"capture_available":null,"note":"Fake payment created by PHPUnit.","customer":{"document":"932221309","email":"alfaro.mara@loya.es.cl","name":"LUNA GRANADOS","birth_date":"1966-05-19"}},"status":"SUCCESS"}'; + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Http/ClientTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Http/ClientTest.php new file mode 100644 index 00000000..757556b1 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Http/ClientTest.php @@ -0,0 +1,61 @@ +inSandboxMode()->getMode(); + $this->assertTrue($subject->isSandbox()); + + $liveMode = $subject->inLiveMode()->getMode(); + $this->assertFalse($subject->isSandbox()); + + $this->assertNotEquals($sandboxMode, $liveMode); + $this->assertEquals($sandboxMode, Client::MODE_SANDBOX); + $this->assertEquals($liveMode, Client::MODE_LIVE); + } + + public function testDefaultUrl() + { + $subject = new Client(); + + $defaultUrl = $subject->getUrl(); + $this->assertTrue($subject->isSandbox()); + + $sandboxUrl = $subject->inSandboxMode()->getUrl(); + $this->assertTrue($subject->isSandbox()); + + $this->assertEquals($sandboxUrl, $defaultUrl); + } + + public function testLiveUrl() + { + $subject = new Client(); + + $sandboxUrl = $subject->inSandboxMode()->getUrl(); + $this->assertTrue($subject->isSandbox()); + + $liveUrl = $subject->inLiveMode()->getUrl(); + $this->assertFalse($subject->isSandbox()); + + $this->assertNotEquals($sandboxUrl, $liveUrl); + } + + public function testFakeRequest() + { + $text = '{"message":"This should be OK"}'; + + $subject = new ClientForTests(new EchoEngine(Client::SANDBOX_URL, $text)); + + $response = $subject->payment((object)['empty' => true]); + $this->assertEquals(json_decode($text, true), $response); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Http/HttpServiceTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Http/HttpServiceTest.php new file mode 100644 index 00000000..b8e97ffe --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Http/HttpServiceTest.php @@ -0,0 +1,29 @@ +assertEquals(Client::MODE_SANDBOX, $client->getMode()); + $http->changeToLive(); + $this->assertEquals(Client::MODE_LIVE, $client->getMode()); + } +} + +class TestHttpService extends HttpService +{ + public function changeToLive() + { + $this->switchMode(false); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/PaymentInfoTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/PaymentInfoTest.php new file mode 100644 index 00000000..d2b8c5a8 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/PaymentInfoTest.php @@ -0,0 +1,52 @@ +config = new Config([ + 'sandboxIntegrationKey' => $env->read('SANDBOX_INTEGRATION_KEY', 'default_integration_key') + ]); + $this->client = new ClientForTests(new EchoEngine(Client::SANDBOX_URL, $text)); + } + + public function testPaymentInfoByHash() + { + $query = new PaymentInfoForTests($this->config, $this->client); + $info = $query->findByHash('5925f3653b2c75e9ce7568d4f16c6d67648b8e92f7f05fa5'); + + $this->assertArrayHasKey('payment', $info); + } + + public function testPaymentInfoByMerchantPaymentCode() + { + $query = new PaymentInfoForTests($this->config, $this->client); + $info = $query->findByMerchantPaymentCode('248b2672f000e293268be28d6048d600'); + + $this->assertArrayHasKey('payment', $info); + } +} + +class PaymentInfoForTests extends PaymentInfo +{ + public function __construct(Config $config, Client $client) + { + $this->client = $client; + parent::__construct($config); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/RefundTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/RefundTest.php new file mode 100644 index 00000000..2978fa68 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/RefundTest.php @@ -0,0 +1,60 @@ +config = new Config([ + 'sandboxIntegrationKey' => $env->read('SANDBOX_INTEGRATION_KEY', 'default_integration_key') + ]); + + $text = '{"payment":{"hash":"59386e0a5f258ffed3bee6fecc9150f916d1e19aa50ec68c","pin":"484913390","merchant_payment_code":"145000015-1496870409","order_number":null,"status":"CO","status_date":"2017-06-07 18:20:13","open_date":"2017-06-07 18:20:10","confirm_date":"2017-06-07 18:20:13","transfer_date":null,"amount_br":"265.00","amount_ext":"80.00","amount_iof":"1.00","currency_rate":"3.3000","currency_ext":"USD","due_date":"2017-06-10","instalments":"1","payment_type_code":"itau","pre_approved":false,"capture_available":null,"user_value_5":"Benjamin","refunds":[{"id":"21425","merchant_refund_code":null,"status":"RE","request_date":"2017-06-08 17:34:59","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"10.23","description":"description"},{"id":"21426","merchant_refund_code":null,"status":"RE","request_date":"2017-06-08 17:36:35","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"10.23","description":"description"}],"customer":{"document":"07834442902","email":"cezar+brbrbrbrbr@ebanx.com","name":"CEZAR LUIZ SAMPAIO","birth_date":"1978-03-29"}},"refund":{"id":"21426","merchant_refund_code":null,"status":"RE","request_date":"2017-06-08 17:36:35","pending_date":null,"confirm_date":null,"cancel_date":null,"amount_ext":"10.23","description":"description"},"operation":"refund","status":"SUCCESS"}'; + $this->client = new ClientForTests(new EchoEngine(Client::SANDBOX_URL, $text)); + } + + public function testRefundByHash() + { + $refund = new RefundForTests($this->config, $this->client); + $result = $refund->requestByHash('59386e0a5f258ffed3bee6fecc9150f916d1e19aa50ec68c', 10.23, 'description'); + + $this->assertArrayHasKey('payment', $result); + } + + public function testRefundByMerchantPaymentCode() + { + $refund = new RefundForTests($this->config, $this->client); + $result = $refund->requestByMerchantPaymentCode('59386e0a5f258ffed3bee6fecc9150f916d1e19aa50ec68c', 10.23, 'description'); + + $this->assertArrayHasKey('payment', $result); + } + + public function testRefundCancel() + { + $refund = new RefundForTests($this->config, $this->client); + $result = $refund->cancel('21425'); + + $this->assertArrayHasKey('payment', $result); + } +} + +class RefundForTests extends Refund +{ + public function __construct(Config $config, Client $client) + { + $this->client = $client; + parent::__construct($config); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Validators/BaseValidatorTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Validators/BaseValidatorTest.php new file mode 100644 index 00000000..f7a27010 --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Validators/BaseValidatorTest.php @@ -0,0 +1,31 @@ +assertFalse($validator->hasErrors()); + $validator->validate(); + $this->assertTrue($validator->hasErrors()); + $this->assertArraySubset([ + 'Error 1', + 'Error 2', + 'Error 3', + ], $validator->getErrors()); + } +} + +class TestValidator extends BaseValidator +{ + public function validate() + { + $this->addError('Error 1'); + $this->addAllErrors(['Error 2', 'Error 3']); + } +} diff --git a/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Validators/ValidationHelperTest.php b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Validators/ValidationHelperTest.php new file mode 100644 index 00000000..8c5f060e --- /dev/null +++ b/lib/Ebanx/vendor/ebanx/benjamin/tests/Unit/Services/Validators/ValidationHelperTest.php @@ -0,0 +1,35 @@ +min(10) + ->max(11) + ->test('Low value', 9) + ); + + $errors = array_merge( + $errors, + $subject->min(10) + ->max(11) + ->test('High value', 12) + ); + + $this->assertContains('Low value', $errors[0]); + $this->assertContains('10', $errors[0]); + + $this->assertContains('High value', $errors[1]); + $this->assertContains('11', $errors[1]); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/CHANGELOG.md b/lib/Ebanx/vendor/guzzlehttp/guzzle/CHANGELOG.md new file mode 100644 index 00000000..f380ad55 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -0,0 +1,1066 @@ +# CHANGELOG + +## 5.3.2 - 2018-01-15 + +* Improve tests +* Fix react promise dependency +* Fix PHP 7 compatibility + +## 5.3.1 - 2016-07-18 + +* Address HTTP_PROXY security vulnerability, CVE-2016-5385: + https://httpoxy.org/ +* Event name fix: https://github.com/guzzle/guzzle/commit/fcae91ff31de41e312fe113ec3acbcda31b2622e +* Response header case sensitivity fix: https://github.com/guzzle/guzzle/commit/043eeadf20ee40ddc6712faee4d3957a91f2b041 + +## 5.3.0 - 2015-05-19 + +* Mock now supports `save_to` +* Marked `AbstractRequestEvent::getTransaction()` as public. +* Fixed a bug in which multiple headers using different casing would overwrite + previous headers in the associative array. +* Added `Utils::getDefaultHandler()` +* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated. +* URL scheme is now always lowercased. + +## 5.2.0 - 2015-01-27 + +* Added `AppliesHeadersInterface` to make applying headers to a request based + on the body more generic and not specific to `PostBodyInterface`. +* Reduced the number of stack frames needed to send requests. +* Nested futures are now resolved in the client rather than the RequestFsm +* Finishing state transitions is now handled in the RequestFsm rather than the + RingBridge. +* Added a guard in the Pool class to not use recursion for request retries. + +## 5.1.0 - 2014-12-19 + +* Pool class no longer uses recursion when a request is intercepted. +* The size of a Pool can now be dynamically adjusted using a callback. + See https://github.com/guzzle/guzzle/pull/943. +* Setting a request option to `null` when creating a request with a client will + ensure that the option is not set. This allows you to overwrite default + request options on a per-request basis. + See https://github.com/guzzle/guzzle/pull/937. +* Added the ability to limit which protocols are allowed for redirects by + specifying a `protocols` array in the `allow_redirects` request option. +* Nested futures due to retries are now resolved when waiting for synchronous + responses. See https://github.com/guzzle/guzzle/pull/947. +* `"0"` is now an allowed URI path. See + https://github.com/guzzle/guzzle/pull/935. +* `Query` no longer typehints on the `$query` argument in the constructor, + allowing for strings and arrays. +* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle + specific exceptions if necessary. + +## 5.0.3 - 2014-11-03 + +This change updates query strings so that they are treated as un-encoded values +by default where the value represents an un-encoded value to send over the +wire. A Query object then encodes the value before sending over the wire. This +means that even value query string values (e.g., ":") are url encoded. This +makes the Query class match PHP's http_build_query function. However, if you +want to send requests over the wire using valid query string characters that do +not need to be encoded, then you can provide a string to Url::setQuery() and +pass true as the second argument to specify that the query string is a raw +string that should not be parsed or encoded (unless a call to getQuery() is +subsequently made, forcing the query-string to be converted into a Query +object). + +## 5.0.2 - 2014-10-30 + +* Added a trailing `\r\n` to multipart/form-data payloads. See + https://github.com/guzzle/guzzle/pull/871 +* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs. +* Status codes are now returned as integers. See + https://github.com/guzzle/guzzle/issues/881 +* No longer overwriting an existing `application/x-www-form-urlencoded` header + when sending POST requests, allowing for customized headers. See + https://github.com/guzzle/guzzle/issues/877 +* Improved path URL serialization. + + * No longer double percent-encoding characters in the path or query string if + they are already encoded. + * Now properly encoding the supplied path to a URL object, instead of only + encoding ' ' and '?'. + * Note: This has been changed in 5.0.3 to now encode query string values by + default unless the `rawString` argument is provided when setting the query + string on a URL: Now allowing many more characters to be present in the + query string without being percent encoded. See http://tools.ietf.org/html/rfc3986#appendix-A + +## 5.0.1 - 2014-10-16 + +Bugfix release. + +* Fixed an issue where connection errors still returned response object in + error and end events event though the response is unusable. This has been + corrected so that a response is not returned in the `getResponse` method of + these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867 +* Fixed an issue where transfer statistics were not being populated in the + RingBridge. https://github.com/guzzle/guzzle/issues/866 + +## 5.0.0 - 2014-10-12 + +Adding support for non-blocking responses and some minor API cleanup. + +### New Features + +* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`. +* Added a public API for creating a default HTTP adapter. +* Updated the redirect plugin to be non-blocking so that redirects are sent + concurrently. Other plugins like this can now be updated to be non-blocking. +* Added a "progress" event so that you can get upload and download progress + events. +* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers + requests concurrently using a capped pool size as efficiently as possible. +* Added `hasListeners()` to EmitterInterface. +* Removed `GuzzleHttp\ClientInterface::sendAll` and marked + `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the + recommended way). + +### Breaking changes + +The breaking changes in this release are relatively minor. The biggest thing to +look out for is that request and response objects no longer implement fluent +interfaces. + +* Removed the fluent interfaces (i.e., `return $this`) from requests, + responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`, + `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and + `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of + why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/. + This also makes the Guzzle message interfaces compatible with the current + PSR-7 message proposal. +* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except + for the HTTP request functions from function.php, these functions are now + implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode` + moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to + `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to + `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be + `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php + caused problems for many users: they aren't PSR-4 compliant, require an + explicit include, and needed an if-guard to ensure that the functions are not + declared multiple times. +* Rewrote adapter layer. + * Removing all classes from `GuzzleHttp\Adapter`, these are now + implemented as callables that are stored in `GuzzleHttp\Ring\Client`. + * Removed the concept of "parallel adapters". Sending requests serially or + concurrently is now handled using a single adapter. + * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The + Transaction object now exposes the request, response, and client as public + properties. The getters and setters have been removed. +* Removed the "headers" event. This event was only useful for changing the + body a response once the headers of the response were known. You can implement + a similar behavior in a number of ways. One example might be to use a + FnStream that has access to the transaction being sent. For example, when the + first byte is written, you could check if the response headers match your + expectations, and if so, change the actual stream body that is being + written to. +* Removed the `asArray` parameter from + `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header + value as an array, then use the newly added `getHeaderAsArray()` method of + `MessageInterface`. This change makes the Guzzle interfaces compatible with + the PSR-7 interfaces. +* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add + custom request options using double-dispatch (this was an implementation + detail). Instead, you should now provide an associative array to the + constructor which is a mapping of the request option name mapping to a + function that applies the option value to a request. +* Removed the concept of "throwImmediately" from exceptions and error events. + This control mechanism was used to stop a transfer of concurrent requests + from completing. This can now be handled by throwing the exception or by + cancelling a pool of requests or each outstanding future request individually. +* Updated to "GuzzleHttp\Streams" 3.0. + * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a + `maxLen` parameter. This update makes the Guzzle streams project + compatible with the current PSR-7 proposal. + * `GuzzleHttp\Stream\Stream::__construct`, + `GuzzleHttp\Stream\Stream::factory`, and + `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second + argument. They now accept an associative array of options, including the + "size" key and "metadata" key which can be used to provide custom metadata. + +## 4.2.2 - 2014-09-08 + +* Fixed a memory leak in the CurlAdapter when reusing cURL handles. +* No longer using `request_fulluri` in stream adapter proxies. +* Relative redirects are now based on the last response, not the first response. + +## 4.2.1 - 2014-08-19 + +* Ensuring that the StreamAdapter does not always add a Content-Type header +* Adding automated github releases with a phar and zip + +## 4.2.0 - 2014-08-17 + +* Now merging in default options using a case-insensitive comparison. + Closes https://github.com/guzzle/guzzle/issues/767 +* Added the ability to automatically decode `Content-Encoding` response bodies + using the `decode_content` request option. This is set to `true` by default + to decode the response body if it comes over the wire with a + `Content-Encoding`. Set this value to `false` to disable decoding the + response content, and pass a string to provide a request `Accept-Encoding` + header and turn on automatic response decoding. This feature now allows you + to pass an `Accept-Encoding` header in the headers of a request but still + disable automatic response decoding. + Closes https://github.com/guzzle/guzzle/issues/764 +* Added the ability to throw an exception immediately when transferring + requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760 +* Updating guzzlehttp/streams dependency to ~2.1 +* No longer utilizing the now deprecated namespaced methods from the stream + package. + +## 4.1.8 - 2014-08-14 + +* Fixed an issue in the CurlFactory that caused setting the `stream=false` + request option to throw an exception. + See: https://github.com/guzzle/guzzle/issues/769 +* TransactionIterator now calls rewind on the inner iterator. + See: https://github.com/guzzle/guzzle/pull/765 +* You can now set the `Content-Type` header to `multipart/form-data` + when creating POST requests to force multipart bodies. + See https://github.com/guzzle/guzzle/issues/768 + +## 4.1.7 - 2014-08-07 + +* Fixed an error in the HistoryPlugin that caused the same request and response + to be logged multiple times when an HTTP protocol error occurs. +* Ensuring that cURL does not add a default Content-Type when no Content-Type + has been supplied by the user. This prevents the adapter layer from modifying + the request that is sent over the wire after any listeners may have already + put the request in a desired state (e.g., signed the request). +* Throwing an exception when you attempt to send requests that have the + "stream" set to true in parallel using the MultiAdapter. +* Only calling curl_multi_select when there are active cURL handles. This was + previously changed and caused performance problems on some systems due to PHP + always selecting until the maximum select timeout. +* Fixed a bug where multipart/form-data POST fields were not correctly + aggregated (e.g., values with "&"). + +## 4.1.6 - 2014-08-03 + +* Added helper methods to make it easier to represent messages as strings, + including getting the start line and getting headers as a string. + +## 4.1.5 - 2014-08-02 + +* Automatically retrying cURL "Connection died, retrying a fresh connect" + errors when possible. +* cURL implementation cleanup +* Allowing multiple event subscriber listeners to be registered per event by + passing an array of arrays of listener configuration. + +## 4.1.4 - 2014-07-22 + +* Fixed a bug that caused multi-part POST requests with more than one field to + serialize incorrectly. +* Paths can now be set to "0" +* `ResponseInterface::xml` now accepts a `libxml_options` option and added a + missing default argument that was required when parsing XML response bodies. +* A `save_to` stream is now created lazily, which means that files are not + created on disk unless a request succeeds. + +## 4.1.3 - 2014-07-15 + +* Various fixes to multipart/form-data POST uploads +* Wrapping function.php in an if-statement to ensure Guzzle can be used + globally and in a Composer install +* Fixed an issue with generating and merging in events to an event array +* POST headers are only applied before sending a request to allow you to change + the query aggregator used before uploading +* Added much more robust query string parsing +* Fixed various parsing and normalization issues with URLs +* Fixing an issue where multi-valued headers were not being utilized correctly + in the StreamAdapter + +## 4.1.2 - 2014-06-18 + +* Added support for sending payloads with GET requests + +## 4.1.1 - 2014-06-08 + +* Fixed an issue related to using custom message factory options in subclasses +* Fixed an issue with nested form fields in a multi-part POST +* Fixed an issue with using the `json` request option for POST requests +* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar` + +## 4.1.0 - 2014-05-27 + +* Added a `json` request option to easily serialize JSON payloads. +* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON. +* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`. +* Added the ability to provide an emitter to a client in the client constructor. +* Added the ability to persist a cookie session using $_SESSION. +* Added a trait that can be used to add event listeners to an iterator. +* Removed request method constants from RequestInterface. +* Fixed warning when invalid request start-lines are received. +* Updated MessageFactory to work with custom request option methods. +* Updated cacert bundle to latest build. + +4.0.2 (2014-04-16) +------------------ + +* Proxy requests using the StreamAdapter now properly use request_fulluri (#632) +* Added the ability to set scalars as POST fields (#628) + +## 4.0.1 - 2014-04-04 + +* The HTTP status code of a response is now set as the exception code of + RequestException objects. +* 303 redirects will now correctly switch from POST to GET requests. +* The default parallel adapter of a client now correctly uses the MultiAdapter. +* HasDataTrait now initializes the internal data array as an empty array so + that the toArray() method always returns an array. + +## 4.0.0 - 2014-03-29 + +* For more information on the 4.0 transition, see: + http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/ +* For information on changes and upgrading, see: + https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 +* Added `GuzzleHttp\batch()` as a convenience function for sending requests in + parallel without needing to write asynchronous code. +* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`. + You can now pass a callable or an array of associative arrays where each + associative array contains the "fn", "priority", and "once" keys. + +## 4.0.0.rc-2 - 2014-03-25 + +* Removed `getConfig()` and `setConfig()` from clients to avoid confusion + around whether things like base_url, message_factory, etc. should be able to + be retrieved or modified. +* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface +* functions.php functions were renamed using snake_case to match PHP idioms +* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and + `GUZZLE_CURL_SELECT_TIMEOUT` environment variables +* Added the ability to specify custom `sendAll()` event priorities +* Added the ability to specify custom stream context options to the stream + adapter. +* Added a functions.php function for `get_path()` and `set_path()` +* CurlAdapter and MultiAdapter now use a callable to generate curl resources +* MockAdapter now properly reads a body and emits a `headers` event +* Updated Url class to check if a scheme and host are set before adding ":" + and "//". This allows empty Url (e.g., "") to be serialized as "". +* Parsing invalid XML no longer emits warnings +* Curl classes now properly throw AdapterExceptions +* Various performance optimizations +* Streams are created with the faster `Stream\create()` function +* Marked deprecation_proxy() as internal +* Test server is now a collection of static methods on a class + +## 4.0.0-rc.1 - 2014-03-15 + +* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 + +## 3.8.1 - 2014-01-28 + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +## 3.8.0 - 2013-12-05 + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +## 3.7.4 - 2013-10-02 + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +## 3.7.3 - 2013-09-08 + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +## 3.7.2 - 2013-08-02 + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +## 3.7.1 - 2013-07-05 + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +## 3.7.0 - 2013-06-10 + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc.). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc.). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +## 3.6.0 - 2013-05-29 + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +## 3.5.0 - 2013-05-13 + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +## 3.4.3 - 2013-04-30 + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +## 3.4.2 - 2013-04-29 + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +## 3.4.1 - 2013-04-16 + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +## 3.4.0 - 2013-04-11 + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +## 3.3.1 - 2013-03-10 + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +## 3.3.0 - 2013-03-03 + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +## 3.2.0 - 2013-02-14 + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +## 3.1.2 - 2013-01-27 + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +## 3.1.1 - 2013-01-20 + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +## 3.1.0 - 2013-01-12 + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +## 3.0.7 - 2012-12-19 + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +## 3.0.6 - 2012-12-09 + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +## 3.0.5 - 2012-11-18 + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +## 3.0.4 - 2012-11-11 + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +## 3.0.3 - 2012-11-04 + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +## 3.0.2 - 2012-10-25 + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +## 3.0.1 - 2012-10-22 + +* Models can now be used like regular collection objects by calling filter, map, etc. +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +## 3.0.0 - 2012-10-15 + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +## 2.8.8 - 2012-10-15 + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +## 2.8.7 - 2012-09-30 + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +## 2.8.6 - 2012-09-05 + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +## 2.8.5 - 2012-08-29 + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +## 2.8.4 - 2012-08-15 + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +## 2.8.3 - 2012-07-30 + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +## 2.8.2 - 2012-07-24 + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()` +* Bug: `+` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +## 2.8.1 - 2012-07-16 + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +## 2.8.0 - 2012-07-15 + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +## 2.7.2 - 2012-07-02 + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +## 2.7.1 - 2012-06-26 + +* Minor patch to update version number in UA string +* Updating build process + +## 2.7.0 - 2012-06-25 + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +## 2.6.6 - 2012-06-10 + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +## 2.6.5 - 2012-06-03 + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +## 2.6.4 - 2012-05-30 + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +## 2.6.3 - 2012-05-23 + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +## 2.6.2 - 2012-05-19 + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +## 2.6.1 - 2012-05-19 + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +## 2.6.0 - 2012-05-15 + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +## 2.5.0 - 2012-05-08 + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/LICENSE b/lib/Ebanx/vendor/guzzlehttp/guzzle/LICENSE new file mode 100644 index 00000000..9af9fba6 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2015 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/Makefile b/lib/Ebanx/vendor/guzzlehttp/guzzle/Makefile new file mode 100644 index 00000000..69bf3273 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/Makefile @@ -0,0 +1,50 @@ +all: clean coverage docs + +start-server: + cd vendor/guzzlehttp/ringphp && make start-server + +stop-server: + cd vendor/guzzlehttp/ringphp && make stop-server + +test: start-server + vendor/bin/phpunit + $(MAKE) stop-server + +coverage: start-server + vendor/bin/phpunit --coverage-html=artifacts/coverage + $(MAKE) stop-server + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* + +docs: + cd docs && make html && cd .. + +view-docs: + open docs/_build/html/index.html + +tag: + $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) + @echo Tagging $(TAG) + chag update $(TAG) + sed -i '' -e "s/VERSION = '.*'/VERSION = '$(TAG)'/" src/ClientInterface.php + php -l src/ClientInterface.php + git add -A + git commit -m '$(TAG) release' + chag tag + +perf: start-server + php tests/perf.php + $(MAKE) stop-server + +package: burgomaster + php build/packager.php + +burgomaster: + mkdir -p build/artifacts + curl -s https://raw.githubusercontent.com/mtdowling/Burgomaster/0.0.2/src/Burgomaster.php > build/artifacts/Burgomaster.php + +.PHONY: docs burgomaster diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/README.md b/lib/Ebanx/vendor/guzzlehttp/guzzle/README.md new file mode 100644 index 00000000..d41e7e75 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/README.md @@ -0,0 +1,70 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +[![Build Status](https://secure.travis-ci.org/guzzle/guzzle.svg?branch=master)](http://travis-ci.org/guzzle/guzzle) + +Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and +trivial to integrate with web services. + +- Manages things like persistent connections, represents query strings as + collections, simplifies sending streaming POST requests with fields and + files, and abstracts away the underlying HTTP transport layer. +- Can send both synchronous and asynchronous requests using the same interface + without requiring a dependency on a specific event loop. +- Pluggable HTTP adapters allows Guzzle to integrate with any method you choose + for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream + wrapper, non-blocking event loops like ReactPHP. +- Guzzle makes it so that you no longer need to fool around with cURL options, + stream contexts, or sockets. + +```php +$client = new GuzzleHttp\Client(); +$response = $client->get('http://guzzlephp.org'); +$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); +echo $res->getStatusCode(); +// "200" +echo $res->getHeader('content-type'); +// 'application/json; charset=utf8' +echo $res->getBody(); +// {"type":"User"...' +var_export($res->json()); +// Outputs the JSON decoded data + +// Send an asynchronous request. +$req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]); +$client->send($req)->then(function ($response) { + echo 'I completed! ' . $response; +}); +``` + +Get more information and answers with the +[Documentation](http://guzzlephp.org/), +[Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), +and [Gitter](https://gitter.im/guzzle/guzzle). + +### Installing via Composer + +The recommended way to install Guzzle is through +[Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php +``` + +Next, run the Composer command to install the latest stable version of Guzzle: + +```bash +composer.phar require guzzlehttp/guzzle +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` + +### Documentation + +More information can be found in the online documentation at +http://guzzlephp.org/. diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/UPGRADING.md b/lib/Ebanx/vendor/guzzlehttp/guzzle/UPGRADING.md new file mode 100644 index 00000000..2b3877fa --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -0,0 +1,1050 @@ +Guzzle Upgrade Guide +==================== + +4.x to 5.0 +---------- + +## Rewritten Adapter Layer + +Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send +HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor +is still supported, but it has now been renamed to `handler`. Instead of +passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP +`callable` that follows the RingPHP specification. + +## Removed Fluent Interfaces + +[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil) +from the following classes: + +- `GuzzleHttp\Collection` +- `GuzzleHttp\Url` +- `GuzzleHttp\Query` +- `GuzzleHttp\Post\PostBody` +- `GuzzleHttp\Cookie\SetCookie` + +## Removed functions.php + +Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following +functions can be used as replacements. + +- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode` +- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath` +- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path` +- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however, + deprecated in favor of using `GuzzleHttp\Pool::batch()`. + +The "procedural" global client has been removed with no replacement (e.g., +`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client` +object as a replacement. + +## `throwImmediately` has been removed + +The concept of "throwImmediately" has been removed from exceptions and error +events. This control mechanism was used to stop a transfer of concurrent +requests from completing. This can now be handled by throwing the exception or +by cancelling a pool of requests or each outstanding future request +individually. + +## headers event has been removed + +Removed the "headers" event. This event was only useful for changing the +body a response once the headers of the response were known. You can implement +a similar behavior in a number of ways. One example might be to use a +FnStream that has access to the transaction being sent. For example, when the +first byte is written, you could check if the response headers match your +expectations, and if so, change the actual stream body that is being +written to. + +## Updates to HTTP Messages + +Removed the `asArray` parameter from +`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header +value as an array, then use the newly added `getHeaderAsArray()` method of +`MessageInterface`. This change makes the Guzzle interfaces compatible with +the PSR-7 interfaces. + +3.x to 4.0 +---------- + +## Overarching changes: + +- Now requires PHP 5.4 or greater. +- No longer requires cURL to send requests. +- Guzzle no longer wraps every exception it throws. Only exceptions that are + recoverable are now wrapped by Guzzle. +- Various namespaces have been removed or renamed. +- No longer requiring the Symfony EventDispatcher. A custom event dispatcher + based on the Symfony EventDispatcher is + now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant + speed and functionality improvements). + +Changes per Guzzle 3.x namespace are described below. + +## Batch + +The `Guzzle\Batch` namespace has been removed. This is best left to +third-parties to implement on top of Guzzle's core HTTP library. + +## Cache + +The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement +has been implemented yet, but hoping to utilize a PSR cache interface). + +## Common + +- Removed all of the wrapped exceptions. It's better to use the standard PHP + library for unrecoverable exceptions. +- `FromConfigInterface` has been removed. +- `Guzzle\Common\Version` has been removed. The VERSION constant can be found + at `GuzzleHttp\ClientInterface::VERSION`. + +### Collection + +- `getAll` has been removed. Use `toArray` to convert a collection to an array. +- `inject` has been removed. +- `keySearch` has been removed. +- `getPath` no longer supports wildcard expressions. Use something better like + JMESPath for this. +- `setPath` now supports appending to an existing array via the `[]` notation. + +### Events + +Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses +`GuzzleHttp\Event\Emitter`. + +- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by + `GuzzleHttp\Event\EmitterInterface`. +- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by + `GuzzleHttp\Event\Emitter`. +- `Symfony\Component\EventDispatcher\Event` is replaced by + `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in + `GuzzleHttp\Event\EventInterface`. +- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and + `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the + event emitter of a request, client, etc. now uses the `getEmitter` method + rather than the `getDispatcher` method. + +#### Emitter + +- Use the `once()` method to add a listener that automatically removes itself + the first time it is invoked. +- Use the `listeners()` method to retrieve a list of event listeners rather than + the `getListeners()` method. +- Use `emit()` instead of `dispatch()` to emit an event from an emitter. +- Use `attach()` instead of `addSubscriber()` and `detach()` instead of + `removeSubscriber()`. + +```php +$mock = new Mock(); +// 3.x +$request->getEventDispatcher()->addSubscriber($mock); +$request->getEventDispatcher()->removeSubscriber($mock); +// 4.x +$request->getEmitter()->attach($mock); +$request->getEmitter()->detach($mock); +``` + +Use the `on()` method to add a listener rather than the `addListener()` method. + +```php +// 3.x +$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } ); +// 4.x +$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } ); +``` + +## Http + +### General changes + +- The cacert.pem certificate has been moved to `src/cacert.pem`. +- Added the concept of adapters that are used to transfer requests over the + wire. +- Simplified the event system. +- Sending requests in parallel is still possible, but batching is no longer a + concept of the HTTP layer. Instead, you must use the `complete` and `error` + events to asynchronously manage parallel request transfers. +- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`. +- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`. +- QueryAggregators have been rewritten so that they are simply callable + functions. +- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in + `functions.php` for an easy to use static client instance. +- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from + `GuzzleHttp\Exception\TransferException`. + +### Client + +Calling methods like `get()`, `post()`, `head()`, etc. no longer create and +return a request, but rather creates a request, sends the request, and returns +the response. + +```php +// 3.0 +$request = $client->get('/'); +$response = $request->send(); + +// 4.0 +$response = $client->get('/'); + +// or, to mirror the previous behavior +$request = $client->createRequest('GET', '/'); +$response = $client->send($request); +``` + +`GuzzleHttp\ClientInterface` has changed. + +- The `send` method no longer accepts more than one request. Use `sendAll` to + send multiple requests in parallel. +- `setUserAgent()` has been removed. Use a default request option instead. You + could, for example, do something like: + `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`. +- `setSslVerification()` has been removed. Use default request options instead, + like `$client->setConfig('defaults/verify', true)`. + +`GuzzleHttp\Client` has changed. + +- The constructor now accepts only an associative array. You can include a + `base_url` string or array to use a URI template as the base URL of a client. + You can also specify a `defaults` key that is an associative array of default + request options. You can pass an `adapter` to use a custom adapter, + `batch_adapter` to use a custom adapter for sending requests in parallel, or + a `message_factory` to change the factory used to create HTTP requests and + responses. +- The client no longer emits a `client.create_request` event. +- Creating requests with a client no longer automatically utilize a URI + template. You must pass an array into a creational method (e.g., + `createRequest`, `get`, `put`, etc.) in order to expand a URI template. + +### Messages + +Messages no longer have references to their counterparts (i.e., a request no +longer has a reference to it's response, and a response no loger has a +reference to its request). This association is now managed through a +`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to +these transaction objects using request events that are emitted over the +lifecycle of a request. + +#### Requests with a body + +- `GuzzleHttp\Message\EntityEnclosingRequest` and + `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The + separation between requests that contain a body and requests that do not + contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface` + handles both use cases. +- Any method that previously accepts a `GuzzleHttp\Response` object now accept a + `GuzzleHttp\Message\ResponseInterface`. +- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to + `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create + both requests and responses and is implemented in + `GuzzleHttp\Message\MessageFactory`. +- POST field and file methods have been removed from the request object. You + must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface` + to control the format of a POST body. Requests that are created using a + standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use + a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if + the method is POST and no body is provided. + +```php +$request = $client->createRequest('POST', '/'); +$request->getBody()->setField('foo', 'bar'); +$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r'))); +``` + +#### Headers + +- `GuzzleHttp\Message\Header` has been removed. Header values are now simply + represented by an array of values or as a string. Header values are returned + as a string by default when retrieving a header value from a message. You can + pass an optional argument of `true` to retrieve a header value as an array + of strings instead of a single concatenated string. +- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to + `GuzzleHttp\Post`. This interface has been simplified and now allows the + addition of arbitrary headers. +- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most + of the custom headers are now handled separately in specific + subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has + been updated to properly handle headers that contain parameters (like the + `Link` header). + +#### Responses + +- `GuzzleHttp\Message\Response::getInfo()` and + `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event + system to retrieve this type of information. +- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed. +- `GuzzleHttp\Message\Response::getMessage()` has been removed. +- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific + methods have moved to the CacheSubscriber. +- Header specific helper functions like `getContentMd5()` have been removed. + Just use `getHeader('Content-MD5')` instead. +- `GuzzleHttp\Message\Response::setRequest()` and + `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event + system to work with request and response objects as a transaction. +- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the + Redirect subscriber instead. +- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have + been removed. Use `getStatusCode()` instead. + +#### Streaming responses + +Streaming requests can now be created by a client directly, returning a +`GuzzleHttp\Message\ResponseInterface` object that contains a body stream +referencing an open PHP HTTP stream. + +```php +// 3.0 +use Guzzle\Stream\PhpStreamRequestFactory; +$request = $client->get('/'); +$factory = new PhpStreamRequestFactory(); +$stream = $factory->fromRequest($request); +$data = $stream->read(1024); + +// 4.0 +$response = $client->get('/', ['stream' => true]); +// Read some data off of the stream in the response body +$data = $response->getBody()->read(1024); +``` + +#### Redirects + +The `configureRedirects()` method has been removed in favor of a +`allow_redirects` request option. + +```php +// Standard redirects with a default of a max of 5 redirects +$request = $client->createRequest('GET', '/', ['allow_redirects' => true]); + +// Strict redirects with a custom number of redirects +$request = $client->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 5, 'strict' => true] +]); +``` + +#### EntityBody + +EntityBody interfaces and classes have been removed or moved to +`GuzzleHttp\Stream`. All classes and interfaces that once required +`GuzzleHttp\EntityBodyInterface` now require +`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no +longer uses `GuzzleHttp\EntityBody::factory` but now uses +`GuzzleHttp\Stream\Stream::factory` or even better: +`GuzzleHttp\Stream\create()`. + +- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface` +- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream` +- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream` +- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream` +- `Guzzle\Http\IoEmittyinEntityBody` has been removed. + +#### Request lifecycle events + +Requests previously submitted a large number of requests. The number of events +emitted over the lifecycle of a request has been significantly reduced to make +it easier to understand how to extend the behavior of a request. All events +emitted during the lifecycle of a request now emit a custom +`GuzzleHttp\Event\EventInterface` object that contains context providing +methods and a way in which to modify the transaction at that specific point in +time (e.g., intercept the request and set a response on the transaction). + +- `request.before_send` has been renamed to `before` and now emits a + `GuzzleHttp\Event\BeforeEvent` +- `request.complete` has been renamed to `complete` and now emits a + `GuzzleHttp\Event\CompleteEvent`. +- `request.sent` has been removed. Use `complete`. +- `request.success` has been removed. Use `complete`. +- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`. +- `request.exception` has been removed. Use `error`. +- `request.receive.status_line` has been removed. +- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to + maintain a status update. +- `curl.callback.write` has been removed. Use a custom `StreamInterface` to + intercept writes. +- `curl.callback.read` has been removed. Use a custom `StreamInterface` to + intercept reads. + +`headers` is a new event that is emitted after the response headers of a +request have been received before the body of the response is downloaded. This +event emits a `GuzzleHttp\Event\HeadersEvent`. + +You can intercept a request and inject a response using the `intercept()` event +of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and +`GuzzleHttp\Event\ErrorEvent` event. + +See: http://docs.guzzlephp.org/en/latest/events.html + +## Inflection + +The `Guzzle\Inflection` namespace has been removed. This is not a core concern +of Guzzle. + +## Iterator + +The `Guzzle\Iterator` namespace has been removed. + +- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and + `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of + Guzzle itself. +- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent + class is shipped with PHP 5.4. +- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because + it's easier to just wrap an iterator in a generator that maps values. + +For a replacement of these iterators, see https://github.com/nikic/iter + +## Log + +The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The +`Guzzle\Log` namespace has been removed. Guzzle now relies on +`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been +moved to `GuzzleHttp\Subscriber\Log\Formatter`. + +## Parser + +The `Guzzle\Parser` namespace has been removed. This was previously used to +make it possible to plug in custom parsers for cookies, messages, URI +templates, and URLs; however, this level of complexity is not needed in Guzzle +so it has been removed. + +- Cookie: Cookie parsing logic has been moved to + `GuzzleHttp\Cookie\SetCookie::fromString`. +- Message: Message parsing logic for both requests and responses has been moved + to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only + used in debugging or deserializing messages, so it doesn't make sense for + Guzzle as a library to add this level of complexity to parsing messages. +- UriTemplate: URI template parsing has been moved to + `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL + URI template library if it is installed. +- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously + it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary, + then developers are free to subclass `GuzzleHttp\Url`. + +## Plugin + +The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`. +Several plugins are shipping with the core Guzzle library under this namespace. + +- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar + code has moved to `GuzzleHttp\Cookie`. +- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin. +- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is + received. +- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin. +- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before + sending. This subscriber is attached to all requests by default. +- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin. + +The following plugins have been removed (third-parties are free to re-implement +these if needed): + +- `GuzzleHttp\Plugin\Async` has been removed. +- `GuzzleHttp\Plugin\CurlAuth` has been removed. +- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This + functionality should instead be implemented with event listeners that occur + after normal response parsing occurs in the guzzle/command package. + +The following plugins are not part of the core Guzzle package, but are provided +in separate repositories: + +- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be muchs simpler + to build custom retry policies using simple functions rather than various + chained classes. See: https://github.com/guzzle/retry-subscriber +- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to + https://github.com/guzzle/cache-subscriber +- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to + https://github.com/guzzle/log-subscriber +- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to + https://github.com/guzzle/message-integrity-subscriber +- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to + `GuzzleHttp\Subscriber\MockSubscriber`. +- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to + https://github.com/guzzle/oauth-subscriber + +## Service + +The service description layer of Guzzle has moved into two separate packages: + +- http://github.com/guzzle/command Provides a high level abstraction over web + services by representing web service operations using commands. +- http://github.com/guzzle/guzzle-services Provides an implementation of + guzzle/command that provides request serialization and response parsing using + Guzzle service descriptions. + +## Stream + +Stream have moved to a separate package available at +https://github.com/guzzle/streams. + +`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take +on the responsibilities of `Guzzle\Http\EntityBody` and +`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number +of methods implemented by the `StreamInterface` has been drastically reduced to +allow developers to more easily extend and decorate stream behavior. + +## Removed methods from StreamInterface + +- `getStream` and `setStream` have been removed to better encapsulate streams. +- `getMetadata` and `setMetadata` have been removed in favor of + `GuzzleHttp\Stream\MetadataStreamInterface`. +- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been + removed. This data is accessible when + using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`. +- `rewind` has been removed. Use `seek(0)` for a similar behavior. + +## Renamed methods + +- `detachStream` has been renamed to `detach`. +- `feof` has been renamed to `eof`. +- `ftell` has been renamed to `tell`. +- `readLine` has moved from an instance method to a static class method of + `GuzzleHttp\Stream\Stream`. + +## Metadata streams + +`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams +that contain additional metadata accessible via `getMetadata()`. +`GuzzleHttp\Stream\StreamInterface::getMetadata` and +`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed. + +## StreamRequestFactory + +The entire concept of the StreamRequestFactory has been removed. The way this +was used in Guzzle 3 broke the actual interface of sending streaming requests +(instead of getting back a Response, you got a StreamInterface). Streeaming +PHP requests are now implemented throught the `GuzzleHttp\Adapter\StreamAdapter`. + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc.). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml + + + + + + Get a list of groups + + + Uses a search query to get a list of groups + + + + Create a group + + + + + Delete a group by ID + + + + + + + Update a group + + + + + + +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/build/packager.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/build/packager.php new file mode 100644 index 00000000..724bf634 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/build/packager.php @@ -0,0 +1,21 @@ +deepCopy($file, $file); +} + +// Copy each dependency to the staging directory. Copy *.php and *.pem files. +$packager->recursiveCopy('src', 'GuzzleHttp', ['php']); +$packager->recursiveCopy('vendor/react/promise/src', 'React/Promise'); +$packager->recursiveCopy('vendor/guzzlehttp/ringphp/src', 'GuzzleHttp/Ring'); +$packager->recursiveCopy('vendor/guzzlehttp/streams/src', 'GuzzleHttp/Stream'); +$packager->createAutoloader(['React/Promise/functions.php']); +$packager->createPhar(__DIR__ . '/artifacts/guzzle.phar'); +$packager->createZip(__DIR__ . '/artifacts/guzzle.zip'); diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/composer.json b/lib/Ebanx/vendor/guzzlehttp/guzzle/composer.json new file mode 100644 index 00000000..d8bb1203 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/composer.json @@ -0,0 +1,38 @@ +{ + "name": "guzzlehttp/guzzle", + "type": "library", + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0", + "guzzlehttp/ringphp": "^1.1", + "react/promise": "^2.2" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\": "tests/" + } + }, + "scripts": { + "test": "make test", + "test-ci": "make coverage" + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/Makefile b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/Makefile new file mode 100644 index 00000000..d92e03f9 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png new file mode 100644 index 00000000..f1017f7e Binary files /dev/null and b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png differ diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_static/logo.png b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_static/logo.png new file mode 100644 index 00000000..965a4ef4 Binary files /dev/null and b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_static/logo.png differ diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html new file mode 100644 index 00000000..7950a0f8 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html @@ -0,0 +1,3 @@ +
  • GitHub
  • +
  • Forum
  • +
  • IRC
  • diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/clients.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/clients.rst new file mode 100644 index 00000000..35a9806f --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/clients.rst @@ -0,0 +1,1326 @@ +======= +Clients +======= + +Clients are used to create requests, create transactions, send requests +through an HTTP handler, and return a response. You can add default request +options to a client that are applied to every request (e.g., default headers, +default query string parameters, etc.), and you can add event listeners and +subscribers to every request created by a client. + +Creating a client +================= + +The constructor of a client accepts an associative array of configuration +options. + +base_url + Configures a base URL for the client so that requests created + using a relative URL are combined with the ``base_url`` of the client + according to section `5.2 of RFC 3986 `_. + + .. code-block:: php + + // Create a client with a base URL + $client = new GuzzleHttp\Client(['base_url' => 'https://github.com']); + // Send a request to https://github.com/notifications + $response = $client->get('/notifications'); + + Don't feel like reading RFC 3986? Here are some quick examples on how a + ``base_url`` is resolved with another URI. + + ======================= ================== =============================== + base_url URI Result + ======================= ================== =============================== + ``http://foo.com`` ``/bar`` ``http://foo.com/bar`` + ``http://foo.com/foo`` ``/bar`` ``http://foo.com/bar`` + ``http://foo.com/foo`` ``bar`` ``http://foo.com/bar`` + ``http://foo.com/foo/`` ``bar`` ``http://foo.com/foo/bar`` + ``http://foo.com`` ``http://baz.com`` ``http://baz.com`` + ``http://foo.com/?bar`` ``bar`` ``http://foo.com/bar`` + ======================= ================== =============================== + +handler + Configures the `RingPHP handler `_ + used to transfer the HTTP requests of a client. Guzzle will, by default, + utilize a stacked handlers that chooses the best handler to use based on the + provided request options and based on the extensions available in the + environment. + +message_factory + Specifies the factory used to create HTTP requests and responses + (``GuzzleHttp\Message\MessageFactoryInterface``). + +defaults + Associative array of :ref:`request-options` that are applied to every + request created by the client. This allows you to specify things like + default headers (e.g., User-Agent), default query string parameters, SSL + configurations, and any other supported request options. + +emitter + Specifies an event emitter (``GuzzleHttp\Event\EmitterInterface``) instance + to be used by the client to emit request events. This option is useful if + you need to inject an emitter with listeners/subscribers already attached. + +Here's an example of creating a client with various options. + +.. code-block:: php + + use GuzzleHttp\Client; + + $client = new Client([ + 'base_url' => ['https://api.twitter.com/{version}/', ['version' => 'v1.1']], + 'defaults' => [ + 'headers' => ['Foo' => 'Bar'], + 'query' => ['testing' => '123'], + 'auth' => ['username', 'password'], + 'proxy' => 'tcp://localhost:80' + ] + ]); + +Sending Requests +================ + +Requests can be created using various methods of a client. You can create +**and** send requests using one of the following methods: + +- ``GuzzleHttp\Client::get``: Sends a GET request. +- ``GuzzleHttp\Client::head``: Sends a HEAD request +- ``GuzzleHttp\Client::post``: Sends a POST request +- ``GuzzleHttp\Client::put``: Sends a PUT request +- ``GuzzleHttp\Client::delete``: Sends a DELETE request +- ``GuzzleHttp\Client::options``: Sends an OPTIONS request + +Each of the above methods accepts a URL as the first argument and an optional +associative array of :ref:`request-options` as the second argument. + +Synchronous Requests +-------------------- + +Guzzle sends synchronous (blocking) requests when the ``future`` request option +is not specified. This means that the request will complete immediately, and if +an error is encountered, a ``GuzzleHttp\Exception\RequestException`` will be +thrown. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + + $client->put('http://httpbin.org', [ + 'headers' => ['X-Foo' => 'Bar'], + 'body' => 'this is the body!', + 'save_to' => '/path/to/local/file', + 'allow_redirects' => false, + 'timeout' => 5 + ]); + +Synchronous Error Handling +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a recoverable error is encountered while calling the ``send()`` method of +a client, a ``GuzzleHttp\Exception\RequestException`` is thrown. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Exception\RequestException; + + $client = new Client(); + + try { + $client->get('http://httpbin.org'); + } catch (RequestException $e) { + echo $e->getRequest() . "\n"; + if ($e->hasResponse()) { + echo $e->getResponse() . "\n"; + } + } + +``GuzzleHttp\Exception\RequestException`` always contains a +``GuzzleHttp\Message\RequestInterface`` object that can be accessed using the +exception's ``getRequest()`` method. + +A response might be present in the exception. In the event of a networking +error, no response will be received. You can check if a ``RequestException`` +has a response using the ``hasResponse()`` method. If the exception has a +response, then you can access the associated +``GuzzleHttp\Message\ResponseInterface`` using the ``getResponse()`` method of +the exception. + +Asynchronous Requests +--------------------- + +You can send asynchronous requests by setting the ``future`` request option +to ``true`` (or a string that your handler understands). This creates a +``GuzzleHttp\Message\FutureResponse`` object that has not yet completed. Once +you have a future response, you can use a promise object obtained by calling +the ``then`` method of the response to take an action when the response has +completed or encounters an error. + +.. code-block:: php + + $response = $client->put('http://httpbin.org/get', ['future' => true]); + + // Call the function when the response completes + $response->then(function ($response) { + echo $response->getStatusCode(); + }); + +You can call the ``wait()`` method of a future response to block until it has +completed. You also use a future response object just like a normal response +object by accessing the methods of the response. Using a future response like a +normal response object, also known as *dereferencing*, will block until the +response has completed. + +.. code-block:: php + + $response = $client->put('http://httpbin.org/get', ['future' => true]); + + // Block until the response has completed + echo $response->getStatusCode(); + +.. important:: + + If an exception occurred while transferring the future response, then the + exception encountered will be thrown when dereferencing. + +.. note:: + + It depends on the RingPHP handler used by a client, but you typically need + to use the same RingPHP handler in order to utilize asynchronous requests + across multiple clients. + +Asynchronous Error Handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Handling errors with future response object promises is a bit different. When +using a promise, exceptions are forwarded to the ``$onError`` function provided +to the second argument of the ``then()`` function. + +.. code-block:: php + + $response = $client->put('http://httpbin.org/get', ['future' => true]); + + $response + ->then( + function ($response) { + // This is called when the request succeeded + echo 'Success: ' . $response->getStatusCode(); + // Returning a value will forward the value to the next promise + // in the chain. + return $response; + }, + function ($error) { + // This is called when the exception failed. + echo 'Exception: ' . $error->getMessage(); + // Throwing will "forward" the exception to the next promise + // in the chain. + throw $error; + } + ) + ->then( + function($response) { + // This is called after the first promise in the chain. It + // receives the value returned from the first promise. + echo $response->getReasonPhrase(); + }, + function ($error) { + // This is called if the first promise error handler in the + // chain rethrows the exception. + echo 'Error: ' . $error->getMessage(); + } + ); + +Please see the `React/Promises project documentation `_ +for more information on how promise resolution and rejection forwarding works. + +HTTP Errors +----------- + +If the ``exceptions`` request option is not set to ``false``, then exceptions +are thrown for HTTP protocol errors as well: +``GuzzleHttp\Exception\ClientErrorResponseException`` for 4xx level HTTP +responses and ``GuzzleHttp\Exception\ServerException`` for 5xx level responses, +both of which extend from ``GuzzleHttp\Exception\BadResponseException``. + +Creating Requests +----------------- + +You can create a request without sending it. This is useful for building up +requests over time or sending requests in concurrently. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org', [ + 'headers' => ['X-Foo' => 'Bar'] + ]); + + // Modify the request as needed + $request->setHeader('Baz', 'bar'); + +After creating a request, you can send it with the client's ``send()`` method. + +.. code-block:: php + + $response = $client->send($request); + +Sending Requests With a Pool +============================ + +You can send requests concurrently using a fixed size pool via the +``GuzzleHttp\Pool`` class. The Pool class is an implementation of +``GuzzleHttp\Ring\Future\FutureInterface``, meaning it can be dereferenced at a +later time or cancelled before sending. The Pool constructor accepts a client +object, iterator or array that yields ``GuzzleHttp\Message\RequestInterface`` +objects, and an optional associative array of options that can be used to +affect the transfer. + +.. code-block:: php + + use GuzzleHttp\Pool; + + $requests = [ + $client->createRequest('GET', 'http://httpbin.org'), + $client->createRequest('DELETE', 'http://httpbin.org/delete'), + $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'test']) + ]; + + $options = []; + + // Create a pool. Note: the options array is optional. + $pool = new Pool($client, $requests, $options); + + // Send the requests + $pool->wait(); + +The Pool constructor accepts the following associative array of options: + +- **pool_size**: Integer representing the maximum number of requests that are + allowed to be sent concurrently. +- **before**: Callable or array representing the event listeners to add to + each request's :ref:`before_event` event. +- **complete**: Callable or array representing the event listeners to add to + each request's :ref:`complete_event` event. +- **error**: Callable or array representing the event listeners to add to + each request's :ref:`error_event` event. +- **end**: Callable or array representing the event listeners to add to + each request's :ref:`end_event` event. + +The "before", "complete", "error", and "end" event options accept a callable or +an array of associative arrays where each associative array contains a "fn" key +with a callable value, an optional "priority" key representing the event +priority (with a default value of 0), and an optional "once" key that can be +set to true so that the event listener will be removed from the request after +it is first triggered. + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Event\CompleteEvent; + + // Add a single event listener using a callable. + Pool::send($client, $requests, [ + 'complete' => function (CompleteEvent $event) { + echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; + echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; + } + ]); + + // The above is equivalent to the following, but the following structure + // allows you to add multiple event listeners to the same event name. + Pool::send($client, $requests, [ + 'complete' => [ + [ + 'fn' => function (CompleteEvent $event) { /* ... */ }, + 'priority' => 0, // Optional + 'once' => false // Optional + ] + ] + ]); + +Asynchronous Response Handling +------------------------------ + +When sending requests concurrently using a pool, the request/response/error +lifecycle must be handled asynchronously. This means that you give the Pool +multiple requests and handle the response or errors that is associated with the +request using event callbacks. + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Event\ErrorEvent; + + Pool::send($client, $requests, [ + 'complete' => function (CompleteEvent $event) { + echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; + echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; + // Do something with the completion of the request... + }, + 'error' => function (ErrorEvent $event) { + echo 'Request failed: ' . $event->getRequest()->getUrl() . "\n"; + echo $event->getException(); + // Do something to handle the error... + } + ]); + +The ``GuzzleHttp\Event\ErrorEvent`` event object is emitted when an error +occurs during a transfer. With this event, you have access to the request that +was sent, the response that was received (if one was received), access to +transfer statistics, and the ability to intercept the exception with a +different ``GuzzleHttp\Message\ResponseInterface`` object. See :doc:`events` +for more information. + +Handling Errors After Transferring +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It sometimes might be easier to handle all of the errors that occurred during a +transfer after all of the requests have been sent. Here we are adding each +failed request to an array that we can use to process errors later. + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Event\ErrorEvent; + + $errors = []; + Pool::send($client, $requests, [ + 'error' => function (ErrorEvent $event) use (&$errors) { + $errors[] = $event; + } + ]); + + foreach ($errors as $error) { + // Handle the error... + } + +.. _batch-requests: + +Batching Requests +----------------- + +Sometimes you just want to send a few requests concurrently and then process +the results all at once after they've been sent. Guzzle provides a convenience +function ``GuzzleHttp\Pool::batch()`` that makes this very simple: + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Client; + + $client = new Client(); + + $requests = [ + $client->createRequest('GET', 'http://httpbin.org/get'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + // Results is a GuzzleHttp\BatchResults object. + $results = Pool::batch($client, $requests); + + // Can be accessed by index. + echo $results[0]->getStatusCode(); + + // Can be accessed by request. + echo $results->getResult($requests[0])->getStatusCode(); + + // Retrieve all successful responses + foreach ($results->getSuccessful() as $response) { + echo $response->getStatusCode() . "\n"; + } + + // Retrieve all failures. + foreach ($results->getFailures() as $requestException) { + echo $requestException->getMessage() . "\n"; + } + +``GuzzleHttp\Pool::batch()`` accepts an optional associative array of options +in the third argument that allows you to specify the 'before', 'complete', +'error', and 'end' events as well as specify the maximum number of requests to +send concurrently using the 'pool_size' option key. + +.. _request-options: + +Request Options +=============== + +You can customize requests created by a client using **request options**. +Request options control various aspects of a request including, headers, +query string parameters, timeout settings, the body of a request, and much +more. + +All of the following examples use the following client: + +.. code-block:: php + + $client = new GuzzleHttp\Client(['base_url' => 'http://httpbin.org']); + +headers +------- + +:Summary: Associative array of headers to add to the request. Each key is the + name of a header, and each value is a string or array of strings + representing the header field values. +:Types: array +:Defaults: None + +.. code-block:: php + + // Set various headers on a request + $client->get('/get', [ + 'headers' => [ + 'User-Agent' => 'testing/1.0', + 'Accept' => 'application/json', + 'X-Foo' => ['Bar', 'Baz'] + ] + ]); + +body +---- + +:Summary: The ``body`` option is used to control the body of an entity + enclosing request (e.g., PUT, POST, PATCH). +:Types: + - string + - ``fopen()`` resource + - ``GuzzleHttp\Stream\StreamInterface`` + - ``GuzzleHttp\Post\PostBodyInterface`` +:Default: None + +This setting can be set to any of the following types: + +- string + + .. code-block:: php + + // You can send requests that use a string as the message body. + $client->put('/put', ['body' => 'foo']); + +- resource returned from ``fopen()`` + + .. code-block:: php + + // You can send requests that use a stream resource as the body. + $resource = fopen('http://httpbin.org', 'r'); + $client->put('/put', ['body' => $resource]); + +- Array + + Use an array to send POST style requests that use a + ``GuzzleHttp\Post\PostBodyInterface`` object as the body. + + .. code-block:: php + + // You can send requests that use a POST body containing fields & files. + $client->post('/post', [ + 'body' => [ + 'field' => 'abc', + 'other_field' => '123', + 'file_name' => fopen('/path/to/file', 'r') + ] + ]); + +- ``GuzzleHttp\Stream\StreamInterface`` + + .. code-block:: php + + // You can send requests that use a Guzzle stream object as the body + $stream = GuzzleHttp\Stream\Stream::factory('contents...'); + $client->post('/post', ['body' => $stream]); + +json +---- + +:Summary: The ``json`` option is used to easily upload JSON encoded data as the + body of a request. A Content-Type header of ``application/json`` will be + added if no Content-Type header is already present on the message. +:Types: + Any PHP type that can be operated on by PHP's ``json_encode()`` function. +:Default: None + +.. code-block:: php + + $request = $client->createRequest('PUT', '/put', ['json' => ['foo' => 'bar']]); + echo $request->getHeader('Content-Type'); + // application/json + echo $request->getBody(); + // {"foo":"bar"} + +.. note:: + + This request option does not support customizing the Content-Type header + or any of the options from PHP's `json_encode() `_ + function. If you need to customize these settings, then you must pass the + JSON encoded data into the request yourself using the ``body`` request + option and you must specify the correct Content-Type header using the + ``headers`` request option. + +query +----- + +:Summary: Associative array of query string values to add to the request. +:Types: + - array + - ``GuzzleHttp\Query`` +:Default: None + +.. code-block:: php + + // Send a GET request to /get?foo=bar + $client->get('/get', ['query' => ['foo' => 'bar']]); + +Query strings specified in the ``query`` option are combined with any query +string values that are parsed from the URL. + +.. code-block:: php + + // Send a GET request to /get?abc=123&foo=bar + $client->get('/get?abc=123', ['query' => ['foo' => 'bar']]); + +auth +---- + +:Summary: Pass an array of HTTP authentication parameters to use with the + request. The array must contain the username in index [0], the password in + index [1], and you can optionally provide a built-in authentication type in + index [2]. Pass ``null`` to disable authentication for a request. +:Types: + - array + - string + - null +:Default: None + +The built-in authentication types are as follows: + +basic + Use `basic HTTP authentication `_ in + the ``Authorization`` header (the default setting used if none is + specified). + + .. code-block:: php + + $client->get('/get', ['auth' => ['username', 'password']]); + +digest + Use `digest authentication `_ (must be + supported by the HTTP handler). + + .. code-block:: php + + $client->get('/get', ['auth' => ['username', 'password', 'digest']]); + + *This is currently only supported when using the cURL handler, but creating + a replacement that can be used with any HTTP handler is planned.* + +.. important:: + + The authentication type (whether it's provided as a string or as the third + option in an array) is always converted to a lowercase string. Take this + into account when implementing custom authentication types and when + implementing custom message factories. + +Custom Authentication Schemes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also provide a string representing a custom authentication type name. +When using a custom authentication type string, you will need to implement +the authentication method in an event listener that checks the ``auth`` request +option of a request before it is sent. Authentication listeners that require +a request is not modified after they are signed should have a very low priority +to ensure that they are fired last or near last in the event chain. + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\RequestEvents; + + /** + * Custom authentication listener that handles the "foo" auth type. + * + * Listens to the "before" event of a request and only modifies the request + * when the "auth" config setting of the request is "foo". + */ + class FooAuth implements GuzzleHttp\Event\SubscriberInterface + { + private $password; + + public function __construct($password) + { + $this->password = $password; + } + + public function getEvents() + { + return ['before' => ['sign', RequestEvents::SIGN_REQUEST]]; + } + + public function sign(BeforeEvent $e) + { + if ($e->getRequest()->getConfig()['auth'] == 'foo') { + $e->getRequest()->setHeader('X-Foo', 'Foo ' . $this->password); + } + } + } + + $client->getEmitter()->attach(new FooAuth('password')); + $client->get('/', ['auth' => 'foo']); + +Adapter Specific Authentication Schemes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to use authentication methods provided by cURL (e.g., NTLM, GSS, +etc.), then you need to specify a curl handler option in the ``options`` +request option array. See :ref:`config-option` for more information. + +.. _cookies-option: + +cookies +------- + +:Summary: Specifies whether or not cookies are used in a request or what cookie + jar to use or what cookies to send. +:Types: + - bool + - array + - ``GuzzleHttp\Cookie\CookieJarInterface`` +:Default: None + +Set to ``true`` to use a shared cookie session associated with the client. + +.. code-block:: php + + // Enable cookies using the shared cookie jar of the client. + $client->get('/get', ['cookies' => true]); + +Pass an associative array containing cookies to send in the request and start a +new cookie session. + +.. code-block:: php + + // Enable cookies and send specific cookies + $client->get('/get', ['cookies' => ['foo' => 'bar']]); + +Set to a ``GuzzleHttp\Cookie\CookieJarInterface`` object to use an existing +cookie jar. + +.. code-block:: php + + $jar = new GuzzleHttp\Cookie\CookieJar(); + $client->get('/get', ['cookies' => $jar]); + +.. _allow_redirects-option: + +allow_redirects +--------------- + +:Summary: Describes the redirect behavior of a request +:Types: + - bool + - array +:Default: + :: + + [ + 'max' => 5, + 'strict' => false, + 'referer' => true, + 'protocols' => ['http', 'https'] + ] + +Set to ``false`` to disable redirects. + +.. code-block:: php + + $res = $client->get('/redirect/3', ['allow_redirects' => false]); + echo $res->getStatusCode(); + // 302 + +Set to ``true`` (the default setting) to enable normal redirects with a maximum +number of 5 redirects. + +.. code-block:: php + + $res = $client->get('/redirect/3'); + echo $res->getStatusCode(); + // 200 + +Pass an associative array containing the 'max' key to specify the maximum +number of redirects, provide a 'strict' key value to specify whether or not to +use strict RFC compliant redirects (meaning redirect POST requests with POST +requests vs. doing what most browsers do which is redirect POST requests with +GET requests), provide a 'referer' key to specify whether or not the "Referer" +header should be added when redirecting, and provide a 'protocols' array that +specifies which protocols are supported for redirects (defaults to +``['http', 'https']``). + +.. code-block:: php + + $res = $client->get('/redirect/3', [ + 'allow_redirects' => [ + 'max' => 10, // allow at most 10 redirects. + 'strict' => true, // use "strict" RFC compliant redirects. + 'referer' => true, // add a Referer header + 'protocols' => ['https'] // only allow https URLs + ] + ]); + echo $res->getStatusCode(); + // 200 + +decode_content +-------------- + +:Summary: Specify whether or not ``Content-Encoding`` responses (gzip, + deflate, etc.) are automatically decoded. +:Types: + - string + - bool +:Default: ``true`` + +This option can be used to control how content-encoded response bodies are +handled. By default, ``decode_content`` is set to true, meaning any gzipped +or deflated response will be decoded by Guzzle. + +When set to ``false``, the body of a response is never decoded, meaning the +bytes pass through the handler unchanged. + +.. code-block:: php + + // Request gzipped data, but do not decode it while downloading + $client->get('/foo.js', [ + 'headers' => ['Accept-Encoding' => 'gzip'], + 'decode_content' => false + ]); + +When set to a string, the bytes of a response are decoded and the string value +provided to the ``decode_content`` option is passed as the ``Accept-Encoding`` +header of the request. + +.. code-block:: php + + // Pass "gzip" as the Accept-Encoding header. + $client->get('/foo.js', ['decode_content' => 'gzip']); + +.. _save_to-option: + +save_to +------- + +:Summary: Specify where the body of a response will be saved. +:Types: + - string + - ``fopen()`` resource + - ``GuzzleHttp\Stream\StreamInterface`` +:Default: PHP temp stream + +Pass a string to specify the path to a file that will store the contents of the +response body: + +.. code-block:: php + + $client->get('/stream/20', ['save_to' => '/path/to/file']); + +Pass a resource returned from ``fopen()`` to write the response to a PHP stream: + +.. code-block:: php + + $resource = fopen('/path/to/file', 'w'); + $client->get('/stream/20', ['save_to' => $resource]); + +Pass a ``GuzzleHttp\Stream\StreamInterface`` object to stream the response body +to an open Guzzle stream: + +.. code-block:: php + + $resource = fopen('/path/to/file', 'w'); + $stream = GuzzleHttp\Stream\Stream::factory($resource); + $client->get('/stream/20', ['save_to' => $stream]); + +.. _events-option: + +events +------ + +:Summary: An associative array mapping event names to a callable. Or an + associative array containing the 'fn' key that maps to a callable, an + optional 'priority' key used to specify the event priority, and an optional + 'once' key used to specify if the event should remove itself the first time + it is triggered. +:Types: array +:Default: None + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\HeadersEvent; + use GuzzleHttp\Event\CompleteEvent; + use GuzzleHttp\Event\ErrorEvent; + + $client->get('/', [ + 'events' => [ + 'before' => function (BeforeEvent $e) { echo 'Before'; }, + 'complete' => function (CompleteEvent $e) { echo 'Complete'; }, + 'error' => function (ErrorEvent $e) { echo 'Error'; }, + ] + ]); + +Here's an example of using the associative array format for control over the +priority and whether or not an event should be triggered more than once. + +.. code-block:: php + + $client->get('/', [ + 'events' => [ + 'before' => [ + 'fn' => function (BeforeEvent $e) { echo 'Before'; }, + 'priority' => 100, + 'once' => true + ] + ] + ]); + +.. _subscribers-option: + +subscribers +----------- + +:Summary: Array of event subscribers to add to the request. Each value in the + array must be an instance of ``GuzzleHttp\Event\SubscriberInterface``. +:Types: array +:Default: None + +.. code-block:: php + + use GuzzleHttp\Subscriber\History; + use GuzzleHttp\Subscriber\Mock; + use GuzzleHttp\Message\Response; + + $history = new History(); + $mock = new Mock([new Response(200)]); + $client->get('/', ['subscribers' => [$history, $mock]]); + + echo $history; + // Outputs the request and response history + +.. _exceptions-option: + +exceptions +---------- + +:Summary: Set to ``false`` to disable throwing exceptions on an HTTP protocol + errors (i.e., 4xx and 5xx responses). Exceptions are thrown by default when + HTTP protocol errors are encountered. +:Types: bool +:Default: ``true`` + +.. code-block:: php + + $client->get('/status/500'); + // Throws a GuzzleHttp\Exception\ServerException + + $res = $client->get('/status/500', ['exceptions' => false]); + echo $res->getStatusCode(); + // 500 + +.. _timeout-option: + +timeout +------- + +:Summary: Float describing the timeout of the request in seconds. Use ``0`` + to wait indefinitely (the default behavior). +:Types: float +:Default: ``0`` + +.. code-block:: php + + // Timeout if a server does not return a response in 3.14 seconds. + $client->get('/delay/5', ['timeout' => 3.14]); + // PHP Fatal error: Uncaught exception 'GuzzleHttp\Exception\RequestException' + +.. _connect_timeout-option: + +connect_timeout +--------------- + +:Summary: Float describing the number of seconds to wait while trying to connect + to a server. Use ``0`` to wait indefinitely (the default behavior). +:Types: float +:Default: ``0`` + +.. code-block:: php + + // Timeout if the client fails to connect to the server in 3.14 seconds. + $client->get('/delay/5', ['connect_timeout' => 3.14]); + +.. note:: + + This setting must be supported by the HTTP handler used to send a request. + ``connect_timeout`` is currently only supported by the built-in cURL + handler. + +.. _verify-option: + +verify +------ + +:Summary: Describes the SSL certificate verification behavior of a request. + + - Set to ``true`` to enable SSL certificate verification and use the default + CA bundle provided by operating system. + - Set to ``false`` to disable certificate verification (this is insecure!). + - Set to a string to provide the path to a CA bundle to enable verification + using a custom certificate. +:Types: + - bool + - string +:Default: ``true`` + +.. code-block:: php + + // Use the system's CA bundle (this is the default setting) + $client->get('/', ['verify' => true]); + + // Use a custom SSL certificate on disk. + $client->get('/', ['verify' => '/path/to/cert.pem']); + + // Disable validation entirely (don't do this!). + $client->get('/', ['verify' => false]); + +Not all system's have a known CA bundle on disk. For example, Windows and +OS X do not have a single common location for CA bundles. When setting +"verify" to ``true``, Guzzle will do its best to find the most appropriate +CA bundle on your system. When using cURL or the PHP stream wrapper on PHP +versions >= 5.6, this happens by default. When using the PHP stream +wrapper on versions < 5.6, Guzzle tries to find your CA bundle in the +following order: + +1. Check if ``openssl.cafile`` is set in your php.ini file. +2. Check if ``curl.cainfo`` is set in your php.ini file. +3. Check if ``/etc/pki/tls/certs/ca-bundle.crt`` exists (Red Hat, CentOS, + Fedora; provided by the ca-certificates package) +4. Check if ``/etc/ssl/certs/ca-certificates.crt`` exists (Ubuntu, Debian; + provided by the ca-certificates package) +5. Check if ``/usr/local/share/certs/ca-root-nss.crt`` exists (FreeBSD; + provided by the ca_root_nss package) +6. Check if ``/usr/local/etc/openssl/cert.pem`` (OS X; provided by homebrew) +7. Check if ``C:\windows\system32\curl-ca-bundle.crt`` exists (Windows) +8. Check if ``C:\windows\curl-ca-bundle.crt`` exists (Windows) + +The result of this lookup is cached in memory so that subsequent calls +in the same process will return very quickly. However, when sending only +a single request per-process in something like Apache, you should consider +setting the ``openssl.cafile`` environment variable to the path on disk +to the file so that this entire process is skipped. + +If you do not need a specific certificate bundle, then Mozilla provides a +commonly used CA bundle which can be downloaded +`here `_ +(provided by the maintainer of cURL). Once you have a CA bundle available on +disk, you can set the "openssl.cafile" PHP ini setting to point to the path to +the file, allowing you to omit the "verify" request option. Much more detail on +SSL certificates can be found on the +`cURL website `_. + +.. _cert-option: + +cert +---- + +:Summary: Set to a string to specify the path to a file containing a PEM + formatted client side certificate. If a password is required, then set to + an array containing the path to the PEM file in the first array element + followed by the password required for the certificate in the second array + element. +:Types: + - string + - array +:Default: None + +.. code-block:: php + + $client->get('/', ['cert' => ['/path/server.pem', 'password']]); + +.. _ssl_key-option: + +ssl_key +------- + +:Summary: Specify the path to a file containing a private SSL key in PEM + format. If a password is required, then set to an array containing the path + to the SSL key in the first array element followed by the password required + for the certificate in the second element. +:Types: + - string + - array +:Default: None + +.. note:: + + ``ssl_key`` is implemented by HTTP handlers. This is currently only + supported by the cURL handler, but might be supported by other third-part + handlers. + +.. _proxy-option: + +proxy +----- + +:Summary: Pass a string to specify an HTTP proxy, or an array to specify + different proxies for different protocols. +:Types: + - string + - array +:Default: None + +Pass a string to specify a proxy for all protocols. + +.. code-block:: php + + $client->get('/', ['proxy' => 'tcp://localhost:8125']); + +Pass an associative array to specify HTTP proxies for specific URI schemes +(i.e., "http", "https"). + +.. code-block:: php + + $client->get('/', [ + 'proxy' => [ + 'http' => 'tcp://localhost:8125', // Use this proxy with "http" + 'https' => 'tcp://localhost:9124' // Use this proxy with "https" + ] + ]); + +.. note:: + + You can provide proxy URLs that contain a scheme, username, and password. + For example, ``"http://username:password@192.168.16.1:10"``. + +.. _debug-option: + +debug +----- + +:Summary: Set to ``true`` or set to a PHP stream returned by ``fopen()`` to + enable debug output with the handler used to send a request. For example, + when using cURL to transfer requests, cURL's verbose of ``CURLOPT_VERBOSE`` + will be emitted. When using the PHP stream wrapper, stream wrapper + notifications will be emitted. If set to true, the output is written to + PHP's STDOUT. If a PHP stream is provided, output is written to the stream. +:Types: + - bool + - ``fopen()`` resource +:Default: None + +.. code-block:: php + + $client->get('/get', ['debug' => true]); + +Running the above example would output something like the following: + +:: + + * About to connect() to httpbin.org port 80 (#0) + * Trying 107.21.213.98... * Connected to httpbin.org (107.21.213.98) port 80 (#0) + > GET /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0 curl/7.21.4 PHP/5.5.7 + + < HTTP/1.1 200 OK + < Access-Control-Allow-Origin: * + < Content-Type: application/json + < Date: Sun, 16 Feb 2014 06:50:09 GMT + < Server: gunicorn/0.17.4 + < Content-Length: 335 + < Connection: keep-alive + < + * Connection #0 to host httpbin.org left intact + +.. _stream-option: + +stream +------ + +:Summary: Set to ``true`` to stream a response rather than download it all + up-front. +:Types: bool +:Default: ``false`` + +.. code-block:: php + + $response = $client->get('/stream/20', ['stream' => true]); + // Read bytes off of the stream until the end of the stream is reached + $body = $response->getBody(); + while (!$body->eof()) { + echo $body->read(1024); + } + +.. note:: + + Streaming response support must be implemented by the HTTP handler used by + a client. This option might not be supported by every HTTP handler, but the + interface of the response object remains the same regardless of whether or + not it is supported by the handler. + +.. _expect-option: + +expect +------ + +:Summary: Controls the behavior of the "Expect: 100-Continue" header. +:Types: + - bool + - integer +:Default: ``1048576`` + +Set to ``true`` to enable the "Expect: 100-Continue" header for all requests +that sends a body. Set to ``false`` to disable the "Expect: 100-Continue" +header for all requests. Set to a number so that the size of the payload must +be greater than the number in order to send the Expect header. Setting to a +number will send the Expect header for all requests in which the size of the +payload cannot be determined or where the body is not rewindable. + +By default, Guzzle will add the "Expect: 100-Continue" header when the size of +the body of a request is greater than 1 MB and a request is using HTTP/1.1. + +.. note:: + + This option only takes effect when using HTTP/1.1. The HTTP/1.0 and + HTTP/2.0 protocols do not support the "Expect: 100-Continue" header. + Support for handling the "Expect: 100-Continue" workflow must be + implemented by Guzzle HTTP handlers used by a client. + +.. _version-option: + +version +------- + +:Summary: Protocol version to use with the request. +:Types: string, float +:Default: ``1.1`` + +.. code-block:: php + + // Force HTTP/1.0 + $request = $client->createRequest('GET', '/get', ['version' => 1.0]); + echo $request->getProtocolVersion(); + // 1.0 + +.. _config-option: + +config +------ + +:Summary: Associative array of config options that are forwarded to a request's + configuration collection. These values are used as configuration options + that can be consumed by plugins and handlers. +:Types: array +:Default: None + +.. code-block:: php + + $request = $client->createRequest('GET', '/get', ['config' => ['foo' => 'bar']]); + echo $request->getConfig('foo'); + // 'bar' + +Some HTTP handlers allow you to specify custom handler-specific settings. For +example, you can pass custom cURL options to requests by passing an associative +array in the ``config`` request option under the ``curl`` key. + +.. code-block:: php + + // Use custom cURL options with the request. This example uses NTLM auth + // to authenticate with a server. + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_HTTPAUTH => CURLAUTH_NTLM, + CURLOPT_USERPWD => 'username:password' + ] + ] + ]); + +future +------ + +:Summary: Specifies whether or not a response SHOULD be an instance of a + ``GuzzleHttp\Message\FutureResponse`` object. +:Types: + - bool + - string +:Default: ``false`` + +By default, Guzzle requests should be synchronous. You can create asynchronous +future responses by passing the ``future`` request option as ``true``. The +response will only be executed when it is used like a normal response, the +``wait()`` method of the response is called, or the corresponding handler that +created the response is destructing and there are futures that have not been +resolved. + +.. important:: + + This option only has an effect if your handler can create and return future + responses. However, even if a response is completed synchronously, Guzzle + will ensure that a FutureResponse object is returned for API consistency. + +.. code-block:: php + + $response = $client->get('/foo', ['future' => true]) + ->then(function ($response) { + echo 'I got a response! ' . $response; + }); + +Event Subscribers +================= + +Requests emit lifecycle events when they are transferred. A client object has a +``GuzzleHttp\Common\EventEmitter`` object that can be used to add event +*listeners* and event *subscribers* to all requests created by the client. + +.. important:: + + **Every** event listener or subscriber added to a client will be added to + every request created by the client. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\BeforeEvent; + + $client = new Client(); + + // Add a listener that will echo out requests before they are sent + $client->getEmitter()->on('before', function (BeforeEvent $e) { + echo 'About to send request: ' . $e->getRequest(); + }); + + $client->get('http://httpbin.org/get'); + // Outputs the request as a string because of the event + +See :doc:`events` for more information on the event system used in Guzzle. + +Environment Variables +===================== + +Guzzle exposes a few environment variables that can be used to customize the +behavior of the library. + +``GUZZLE_CURL_SELECT_TIMEOUT`` + Controls the duration in seconds that a curl_multi_* handler will use when + selecting on curl handles using ``curl_multi_select()``. Some systems + have issues with PHP's implementation of ``curl_multi_select()`` where + calling this function always results in waiting for the maximum duration of + the timeout. +``HTTP_PROXY`` + Defines the proxy to use when sending requests using the "http" protocol. +``HTTPS_PROXY`` + Defines the proxy to use when sending requests using the "https" protocol. + +Relevant ini Settings +--------------------- + +Guzzle can utilize PHP ini settings when configuring clients. + +``openssl.cafile`` + Specifies the path on disk to a CA file in PEM format to use when sending + requests over "https". See: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/conf.py b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/conf.py new file mode 100644 index 00000000..917bdf4c --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/conf.py @@ -0,0 +1,28 @@ +import sys, os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'Guzzle' +copyright = u'2014, Michael Dowling' +version = '5.0.0' +html_title = "Guzzle Documentation" +html_short_title = "Guzzle" + +exclude_patterns = ['_build'] +html_static_path = ['_static'] + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/events.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/events.rst new file mode 100644 index 00000000..f7bcb803 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/events.rst @@ -0,0 +1,516 @@ +============ +Event System +============ + +Guzzle uses an event emitter to allow you to easily extend the behavior of a +request, change the response associated with a request, and implement custom +error handling. All events in Guzzle are managed and emitted by an +**event emitter**. + +Event Emitters +============== + +Clients, requests, and any other class that implements the +``GuzzleHttp\Event\HasEmitterInterface`` interface have a +``GuzzleHttp\Event\Emitter`` object. You can add event *listeners* and +event *subscribers* to an event *emitter*. + +emitter + An object that implements ``GuzzleHttp\Event\EmitterInterface``. This + object emits named events to event listeners. You may register event + listeners on subscribers on an emitter. + +event listeners + Callable functions that are registered on an event emitter for specific + events. Event listeners are registered on an emitter with a *priority* + setting. If no priority is provided, ``0`` is used by default. + +event subscribers + Classes that tell an event emitter what methods to listen to and what + functions on the class to invoke when the event is triggered. Event + subscribers subscribe event listeners to an event emitter. They should be + used when creating more complex event based logic in applications (i.e., + cookie handling is implemented using an event subscriber because it's + easier to share a subscriber than an anonymous function and because + handling cookies is a complex process). + +priority + Describes the order in which event listeners are invoked when an event is + emitted. The higher a priority value, the earlier the event listener will + be invoked (a higher priority means the listener is more important). If + no priority is provided, the priority is assumed to be ``0``. + + When specifying an event priority, you can pass ``"first"`` or ``"last"`` to + dynamically specify the priority based on the current event priorities + associated with the given event name in the emitter. Use ``"first"`` to set + the priority to the current highest priority plus one. Use ``"last"`` to + set the priority to the current lowest event priority minus one. It is + important to remember that these dynamic priorities are calculated only at + the point of insertion into the emitter and they are not rearranged after + subsequent listeners are added to an emitter. + +propagation + Describes whether or not other event listeners are triggered. Event + emitters will trigger every event listener registered to a specific event + in priority order until all of the listeners have been triggered **or** + until the propagation of an event is stopped. + +Getting an EventEmitter +----------------------- + +You can get the event emitter of ``GuzzleHttp\Event\HasEmitterInterface`` +object using the ``getEmitter()`` method. Here's an example of getting a +client object's event emitter. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + +.. note:: + + You'll notice that the event emitter used in Guzzle is very similar to the + `Symfony2 EventDispatcher component `_. + This is because the Guzzle event system is based on the Symfony2 event + system with several changes. Guzzle uses its own event emitter to improve + performance, isolate Guzzle from changes to the Symfony, and provide a few + improvements that make it easier to use for an HTTP client (e.g., the + addition of the ``once()`` method). + +Adding Event Listeners +---------------------- + +After you have the emitter, you can register event listeners that listen to +specific events using the ``on()`` method. When registering an event listener, +you must tell the emitter what event to listen to (e.g., "before", "after", +"progress", "complete", "error", etc.), what callable to invoke when the +event is triggered, and optionally provide a priority. + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + + $emitter->on('before', function (BeforeEvent $event) { + echo $event->getRequest(); + }); + +When a listener is triggered, it is passed an event that implements the +``GuzzleHttp\Event\EventInterface`` interface, the name of the event, and the +event emitter itself. The above example could more verbosely be written as +follows: + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + + $emitter->on('before', function (BeforeEvent $event, $name) { + echo $event->getRequest(); + }); + +You can add an event listener that automatically removes itself after it is +triggered using the ``once()`` method of an event emitter. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $client->getEmitter()->once('before', function () { + echo 'This will only happen once... per request!'; + }); + +Event Propagation +----------------- + +Event listeners can prevent other event listeners from being triggered by +stopping an event's propagation. + +Stopping event propagation can be useful, for example, if an event listener has +changed the state of the subject to such an extent that allowing subsequent +event listeners to be triggered could place the subject in an inconsistent +state. This technique is used in Guzzle extensively when intercepting error +events with responses. + +You can stop the propagation of an event using the ``stopPropagation()`` method +of a ``GuzzleHttp\Event\EventInterface`` object: + +.. code-block:: php + + use GuzzleHttp\Event\ErrorEvent; + + $emitter->on('error', function (ErrorEvent $event) { + $event->stopPropagation(); + }); + +After stopping the propagation of an event, any subsequent event listeners that +have not yet been triggered will not be triggered. You can check to see if the +propagation of an event was stopped using the ``isPropagationStopped()`` method +of the event. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + // Note: assume that the $errorEvent was created + if ($emitter->emit('error', $errorEvent)->isPropagationStopped()) { + echo 'It was stopped!'; + } + +.. hint:: + + When emitting events, the event that was emitted is returned from the + emitter. This allows you to easily chain calls as shown in the above + example. + +Event Subscribers +----------------- + +Event subscribers are classes that implement the +``GuzzleHttp\Event\SubscriberInterface`` object. They are used to register +one or more event listeners to methods of the class. Event subscribers tell +event emitters exactly which events to listen to and what method to invoke on +the class when the event is triggered by called the ``getEvents()`` method of +a subscriber. + +The following example registers event listeners to the ``before`` and +``complete`` event of a request. When the ``before`` event is emitted, the +``onBefore`` instance method of the subscriber is invoked. When the +``complete`` event is emitted, the ``onComplete`` event of the subscriber is +invoked. Each array value in the ``getEvents()`` return value MUST +contain the name of the method to invoke and can optionally contain the +priority of the listener (as shown in the ``before`` listener in the example). + +.. code-block:: php + + use GuzzleHttp\Event\EmitterInterface; + use GuzzleHttp\Event\SubscriberInterface; + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\CompleteEvent; + + class SimpleSubscriber implements SubscriberInterface + { + public function getEvents() + { + return [ + // Provide name and optional priority + 'before' => ['onBefore', 100], + 'complete' => ['onComplete'], + // You can pass a list of listeners with different priorities + 'error' => [['beforeError', 'first'], ['afterError', 'last']] + ]; + } + + public function onBefore(BeforeEvent $event, $name) + { + echo 'Before!'; + } + + public function onComplete(CompleteEvent $event, $name) + { + echo 'Complete!'; + } + } + +To register the listeners the subscriber needs to be attached to the emitter: + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + $subscriber = new SimpleSubscriber(); + $emitter->attach($subscriber); + + //to remove the listeners + $emitter->detach($subscriber); + +.. note:: + + You can specify event priorities using integers or ``"first"`` and + ``"last"`` to dynamically determine the priority. + +Event Priorities +================ + +When adding event listeners or subscribers, you can provide an optional event +priority. This priority is used to determine how early or late a listener is +triggered. Specifying the correct priority is an important aspect of ensuring +a listener behaves as expected. For example, if you wanted to ensure that +cookies associated with a redirect were added to a cookie jar, you'd need to +make sure that the listener that collects the cookies is triggered before the +listener that performs the redirect. + +In order to help make the process of determining the correct event priority of +a listener easier, Guzzle provides several pre-determined named event +priorities. These priorities are exposed as constants on the +``GuzzleHttp\Event\RequestEvents`` object. + +last + Use ``"last"`` as an event priority to set the priority to the current + lowest event priority minus one. + +first + Use ``"first"`` as an event priority to set the priority to the current + highest priority plus one. + +``GuzzleHttp\Event\RequestEvents::EARLY`` + Used when you want a listener to be triggered as early as possible in the + event chain. + +``GuzzleHttp\Event\RequestEvents::LATE`` + Used when you want a listener to be to be triggered as late as possible in + the event chain. + +``GuzzleHttp\Event\RequestEvents::PREPARE_REQUEST`` + Used when you want a listener to be trigger while a request is being + prepared during the ``before`` event. This event priority is used by the + ``GuzzleHttp\Subscriber\Prepare`` event subscriber which is responsible for + guessing a Content-Type, Content-Length, and Expect header of a request. + You should subscribe after this event is triggered if you want to ensure + that this subscriber has already been triggered. + +``GuzzleHttp\Event\RequestEvents::SIGN_REQUEST`` + Used when you want a listener to be triggered when a request is about to be + signed. Any listener triggered at this point should expect that the request + object will no longer be mutated. If you are implementing a custom + signature subscriber, then you should use this event priority to sign + requests. + +``GuzzleHttp\Event\RequestEvents::VERIFY_RESPONSE`` + Used when you want a listener to be triggered when a response is being + validated during the ``complete`` event. The + ``GuzzleHttp\Subscriber\HttpError`` event subscriber uses this event + priority to check if an exception should be thrown due to a 4xx or 5xx + level response status code. If you are doing any kind of verification of a + response during the complete event, it should happen at this priority. + +``GuzzleHttp\Event\RequestEvents::REDIRECT_RESPONSE`` + Used when you want a listener to be triggered when a response is being + redirected during the ``complete`` event. The + ``GuzzleHttp\Subscriber\Redirect`` event subscriber uses this event + priority when performing redirects. + +You can use the above event priorities as a guideline for determining the +priority of you event listeners. You can use these constants and add to or +subtract from them to ensure that a listener happens before or after the named +priority. + +.. note:: + + "first" and "last" priorities are not adjusted after they added to an + emitter. For example, if you add a listener with a priority of "first", + you can still add subsequent listeners with a higher priority which would + be triggered before the listener added with a priority of "first". + +Working With Request Events +=========================== + +Requests emit lifecycle events when they are transferred. + +.. important:: + + Excluding the ``end`` event, request lifecycle events may be triggered + multiple times due to redirects, retries, or reusing a request multiple + times. Use the ``once()`` method want the event to be triggered once. You + can also remove an event listener from an emitter by using the emitter which + is provided to the listener. + +.. _before_event: + +before +------ + +The ``before`` event is emitted before a request is sent. The event emitted is +a ``GuzzleHttp\Event\BeforeEvent``. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\EmitterInterface; + use GuzzleHttp\Event\BeforeEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/'); + $request->getEmitter()->on( + 'before', + function (BeforeEvent $e, $name) { + echo $name . "\n"; + // "before" + echo $e->getRequest()->getMethod() . "\n"; + // "GET" / "POST" / "PUT" / etc. + echo get_class($e->getClient()); + // "GuzzleHttp\Client" + } + ); + +You can intercept a request with a response before the request is sent over the +wire. The ``intercept()`` method of the ``BeforeEvent`` accepts a +``GuzzleHttp\Message\ResponseInterface``. Intercepting the event will prevent +the request from being sent over the wire and stops the propagation of the +``before`` event, preventing subsequent event listeners from being invoked. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Message\Response; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/status/500'); + $request->getEmitter()->on('before', function (BeforeEvent $e) { + $response = new Response(200); + $e->intercept($response); + }); + + $response = $client->send($request); + echo $response->getStatusCode(); + // 200 + +.. attention:: + + Any exception encountered while executing the ``before`` event will trigger + the ``error`` event of a request. + +.. _complete_event: + +complete +-------- + +The ``complete`` event is emitted after a transaction completes and an entire +response has been received. The event is a ``GuzzleHttp\Event\CompleteEvent``. + +You can intercept the ``complete`` event with a different response if needed +using the ``intercept()`` method of the event. This can be useful, for example, +for changing the response for caching. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\CompleteEvent; + use GuzzleHttp\Message\Response; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/status/302'); + $cachedResponse = new Response(200); + + $request->getEmitter()->on( + 'complete', + function (CompleteEvent $e) use ($cachedResponse) { + if ($e->getResponse()->getStatusCode() == 302) { + // Intercept the original transaction with the new response + $e->intercept($cachedResponse); + } + } + ); + + $response = $client->send($request); + echo $response->getStatusCode(); + // 200 + +.. attention:: + + Any ``GuzzleHttp\Exception\RequestException`` encountered while executing + the ``complete`` event will trigger the ``error`` event of a request. + +.. _error_event: + +error +----- + +The ``error`` event is emitted when a request fails (whether it's from a +networking error or an HTTP protocol error). The event emitted is a +``GuzzleHttp\Event\ErrorEvent``. + +This event is useful for retrying failed requests. Here's an example of +retrying failed basic auth requests by re-sending the original request with +a username and password. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\ErrorEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/basic-auth/foo/bar'); + $request->getEmitter()->on('error', function (ErrorEvent $e) { + if ($e->getResponse()->getStatusCode() == 401) { + // Add authentication stuff as needed and retry the request + $e->getRequest()->setHeader('Authorization', 'Basic ' . base64_encode('foo:bar')); + // Get the client of the event and retry the request + $newResponse = $e->getClient()->send($e->getRequest()); + // Intercept the original transaction with the new response + $e->intercept($newResponse); + } + }); + +.. attention:: + + If an ``error`` event is intercepted with a response, then the ``complete`` + event of a request is triggered. If the ``complete`` event fails, then the + ``error`` event is triggered once again. + +.. _progress_event: + +progress +-------- + +The ``progress`` event is emitted when data is uploaded or downloaded. The +event emitted is a ``GuzzleHttp\Event\ProgressEvent``. + +You can access the emitted progress values using the corresponding public +properties of the event object: + +- ``$downloadSize``: The number of bytes that will be downloaded (if known) +- ``$downloaded``: The number of bytes that have been downloaded +- ``$uploadSize``: The number of bytes that will be uploaded (if known) +- ``$uploaded``: The number of bytes that have been uploaded + +This event cannot be intercepted. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\ProgressEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('PUT', '/put', [ + 'body' => str_repeat('.', 100000) + ]); + + $request->getEmitter()->on('progress', function (ProgressEvent $e) { + echo 'Downloaded ' . $e->downloaded . ' of ' . $e->downloadSize . ' ' + . 'Uploaded ' . $e->uploaded . ' of ' . $e->uploadSize . "\r"; + }); + + $client->send($request); + echo "\n"; + +.. _end_event: + +end +--- + +The ``end`` event is a terminal event, emitted once per request, that provides +access to the response that was received or the exception that was encountered. +The event emitted is a ``GuzzleHttp\Event\EndEvent``. + +This event can be intercepted, but keep in mind that the ``complete`` event +will not fire after intercepting this event. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\EndEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('PUT', '/put', [ + 'body' => str_repeat('.', 100000) + ]); + + $request->getEmitter()->on('end', function (EndEvent $e) { + if ($e->getException()) { + echo 'Error: ' . $e->getException()->getMessage(); + } else { + echo 'Response: ' . $e->getResponse(); + } + }); + + $client->send($request); + echo "\n"; diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/faq.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/faq.rst new file mode 100644 index 00000000..a8e9ad06 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/faq.rst @@ -0,0 +1,199 @@ +=== +FAQ +=== + +Why should I use Guzzle? +======================== + +Guzzle makes it easy to send HTTP requests and super simple to integrate with +web services. Guzzle manages things like persistent connections, represents +query strings as collections, makes it simple to send streaming POST requests +with fields and files, and abstracts away the underlying HTTP transport layer. +By providing an object oriented interface for HTTP clients, requests, responses, +headers, and message bodies, Guzzle makes it so that you no longer need to fool +around with cURL options, stream contexts, or sockets. + +**Asynchronous and Synchronous Requests** + +Guzzle allows you to send both asynchronous and synchronous requests using the +same interface and no direct dependency on an event loop. This flexibility +allows Guzzle to send an HTTP request using the most appropriate HTTP handler +based on the request being sent. For example, when sending synchronous +requests, Guzzle will by default send requests using cURL easy handles to +ensure you're using the fastest possible method for serially transferring HTTP +requests. When sending asynchronous requests, Guzzle might use cURL's multi +interface or any other asynchronous handler you configure. When you request +streaming data, Guzzle will by default use PHP's stream wrapper. + +**Streams** + +Request and response message bodies use :doc:`Guzzle Streams `, +allowing you to stream data without needing to load it all into memory. +Guzzle's stream layer provides a large suite of functionality: + +- You can modify streams at runtime using custom or a number of + pre-made decorators. +- You can emit progress events as data is read from a stream. +- You can validate the integrity of a stream using a rolling hash as data is + read from a stream. + +**Event System and Plugins** + +Guzzle's event system allows you to completely modify the behavior of a client +or request at runtime to cater them for any API. You can send a request with a +client, and the client can do things like automatically retry your request if +it fails, automatically redirect, log HTTP messages that are sent over the +wire, emit progress events as data is uploaded and downloaded, sign requests +using OAuth 1.0, verify the integrity of messages before and after they are +sent over the wire, and anything else you might need. + +**Testable** + +Another important aspect of Guzzle is that it's really +:doc:`easy to test clients `. You can mock HTTP responses and when +testing an handler implementation, Guzzle provides a mock node.js web server. + +**Ecosystem** + +Guzzle has a large `ecosystem of plugins `_, +including `service descriptions `_ +which allows you to abstract web services using service descriptions. These +service descriptions define how to serialize an HTTP request and how to parse +an HTTP response into a more meaningful model object. + +- `Guzzle Command `_: Provides the building + blocks for service description abstraction. +- `Guzzle Services `_: Provides an + implementation of "Guzzle Command" that utilizes Guzzle's service description + format. + +Does Guzzle require cURL? +========================= + +No. Guzzle can use any HTTP handler to send requests. This means that Guzzle +can be used with cURL, PHP's stream wrapper, sockets, and non-blocking libraries +like `React `_. You just need to configure a +`RingPHP `_ handler to use a +different method of sending requests. + +.. note:: + + Guzzle has historically only utilized cURL to send HTTP requests. cURL is + an amazing HTTP client (arguably the best), and Guzzle will continue to use + it by default when it is available. It is rare, but some developers don't + have cURL installed on their systems or run into version specific issues. + By allowing swappable HTTP handlers, Guzzle is now much more customizable + and able to adapt to fit the needs of more developers. + +Can Guzzle send asynchronous requests? +====================================== + +Yes. Pass the ``future`` true request option to a request to send it +asynchronously. Guzzle will then return a ``GuzzleHttp\Message\FutureResponse`` +object that can be used synchronously by accessing the response object like a +normal response, and it can be used asynchronously using a promise that is +notified when the response is resolved with a real response or rejected with an +exception. + +.. code-block:: php + + $request = $client->createRequest('GET', ['future' => true]); + $client->send($request)->then(function ($response) { + echo 'Got a response! ' . $response; + }); + +You can force an asynchronous response to complete using the ``wait()`` method +of a response. + +.. code-block:: php + + $request = $client->createRequest('GET', ['future' => true]); + $futureResponse = $client->send($request); + $futureResponse->wait(); + +How can I add custom cURL options? +================================== + +cURL offer a huge number of `customizable options `_. +While Guzzle normalizes many of these options across different handlers, there +are times when you need to set custom cURL options. This can be accomplished +by passing an associative array of cURL settings in the **curl** key of the +**config** request option. + +For example, let's say you need to customize the outgoing network interface +used with a client. + +.. code-block:: php + + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_INTERFACE => 'xxx.xxx.xxx.xxx' + ] + ] + ]); + +How can I add custom stream context options? +============================================ + +You can pass custom `stream context options `_ +using the **stream_context** key of the **config** request option. The +**stream_context** array is an associative array where each key is a PHP +transport, and each value is an associative array of transport options. + +For example, let's say you need to customize the outgoing network interface +used with a client and allow self-signed certificates. + +.. code-block:: php + + $client->get('/', [ + 'stream' => true, + 'config' => [ + 'stream_context' => [ + 'ssl' => [ + 'allow_self_signed' => true + ], + 'socket' => [ + 'bindto' => 'xxx.xxx.xxx.xxx' + ] + ] + ] + ]); + +Why am I getting an SSL verification error? +=========================================== + +You need to specify the path on disk to the CA bundle used by Guzzle for +verifying the peer certificate. See :ref:`verify-option`. + +What is this Maximum function nesting error? +============================================ + + Maximum function nesting level of '100' reached, aborting + +You could run into this error if you have the XDebug extension installed and +you execute a lot of requests in callbacks. This error message comes +specifically from the XDebug extension. PHP itself does not have a function +nesting limit. Change this setting in your php.ini to increase the limit:: + + xdebug.max_nesting_level = 1000 + +Why am I getting a 417 error response? +====================================== + +This can occur for a number of reasons, but if you are sending PUT, POST, or +PATCH requests with an ``Expect: 100-Continue`` header, a server that does not +support this header will return a 417 response. You can work around this by +setting the ``expect`` request option to ``false``: + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + + // Disable the expect header on a single request + $response = $client->put('/', [], 'the body', [ + 'expect' => false + ]); + + // Disable the expect header on all client requests + $client->setDefaultOption('expect', false) diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/handlers.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/handlers.rst new file mode 100644 index 00000000..d452003f --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/handlers.rst @@ -0,0 +1,43 @@ +================ +RingPHP Handlers +================ + +Guzzle uses RingPHP handlers to send HTTP requests over the wire. +RingPHP provides a low-level library that can be used to "glue" Guzzle with +any transport method you choose. By default, Guzzle utilizes cURL and PHP's +stream wrappers to send HTTP requests. + +RingPHP handlers makes it extremely simple to integrate Guzzle with any +HTTP transport. For example, you could quite easily bridge Guzzle and React +to use Guzzle in React's event loop. + +Using a handler +--------------- + +You can change the handler used by a client using the ``handler`` option in the +``GuzzleHttp\Client`` constructor. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Ring\Client\MockHandler; + + // Create a mock handler that always returns a 200 response. + $handler = new MockHandler(['status' => 200]); + + // Configure to client to use the mock handler. + $client = new Client(['handler' => $handler]); + +At its core, handlers are simply PHP callables that accept a request array +and return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. This future array +can be used just like a normal PHP array, causing it to block, or you can use +the promise interface using the ``then()`` method of the future. Guzzle hooks +up to the RingPHP project using a very simple bridge class +(``GuzzleHttp\RingBridge``). + +Creating a handler +------------------ + +See the `RingPHP `_ project +documentation for more information on creating custom handlers that can be +used with Guzzle clients. diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/http-messages.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/http-messages.rst new file mode 100644 index 00000000..0c6527a8 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/http-messages.rst @@ -0,0 +1,483 @@ +============================= +Request and Response Messages +============================= + +Guzzle is an HTTP client that sends HTTP requests to a server and receives HTTP +responses. Both requests and responses are referred to as messages. + +Headers +======= + +Both request and response messages contain HTTP headers. + +Complex Headers +--------------- + +Some headers contain additional key value pair information. For example, Link +headers contain a link and several key value pairs: + +:: + + ; rel="thing"; type="image/jpeg" + +Guzzle provides a convenience feature that can be used to parse these types of +headers: + +.. code-block:: php + + use GuzzleHttp\Message\Request; + + $request = new Request('GET', '/', [ + 'Link' => '; rel="front"; type="image/jpeg"' + ]); + + $parsed = Request::parseHeader($request, 'Link'); + var_export($parsed); + +Will output: + +.. code-block:: php + + array ( + 0 => + array ( + 0 => '', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + ) + +The result contains a hash of key value pairs. Header values that have no key +(i.e., the link) are indexed numerically while headers parts that form a key +value pair are added as a key value pair. + +See :ref:`headers` for information on how the headers of a request and response +can be accessed and modified. + +Body +==== + +Both request and response messages can contain a body. + +You can check to see if a request or response has a body using the +``getBody()`` method: + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + if ($response->getBody()) { + echo $response->getBody(); + // JSON string: { ... } + } + +The body used in request and response objects is a +``GuzzleHttp\Stream\StreamInterface``. This stream is used for both uploading +data and downloading data. Guzzle will, by default, store the body of a message +in a stream that uses PHP temp streams. When the size of the body exceeds +2 MB, the stream will automatically switch to storing data on disk rather than +in memory (protecting your application from memory exhaustion). + +You can change the body used in a request or response using the ``setBody()`` +method: + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + $request = $client->createRequest('PUT', 'http://httpbin.org/put'); + $request->setBody(Stream::factory('foo')); + +The easiest way to create a body for a request is using the static +``GuzzleHttp\Stream\Stream::factory()`` method. This method accepts various +inputs like strings, resources returned from ``fopen()``, and other +``GuzzleHttp\Stream\StreamInterface`` objects. + +The body of a request or response can be cast to a string or you can read and +write bytes off of the stream as needed. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + $request = $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'testing...']); + + echo $request->getBody()->read(4); + // test + echo $request->getBody()->read(4); + // ing. + echo $request->getBody()->read(1024); + // .. + var_export($request->eof()); + // true + +You can find out more about Guzzle stream objects in :doc:`streams`. + +Requests +======== + +Requests are sent from a client to a server. Requests include the method to +be applied to a resource, the identifier of the resource, and the protocol +version to use. + +Clients are used to create request messages. More precisely, clients use +a ``GuzzleHttp\Message\MessageFactoryInterface`` to create request messages. +You create requests with a client using the ``createRequest()`` method. + +.. code-block:: php + + // Create a request but don't send it immediately + $request = $client->createRequest('GET', 'http://httpbin.org/get'); + +Request Methods +--------------- + +When creating a request, you are expected to provide the HTTP method you wish +to perform. You can specify any method you'd like, including a custom method +that might not be part of RFC 7231 (like "MOVE"). + +.. code-block:: php + + // Create a request using a completely custom HTTP method + $request = $client->createRequest('MOVE', 'http://httpbin.org/move', ['exceptions' => false]); + + echo $request->getMethod(); + // MOVE + + $response = $client->send($request); + echo $response->getStatusCode(); + // 405 + +You can create and send a request using methods on a client that map to the +HTTP method you wish to use. + +:GET: ``$client->get('http://httpbin.org/get', [/** options **/])`` +:POST: ``$client->post('http://httpbin.org/post', [/** options **/])`` +:HEAD: ``$client->head('http://httpbin.org/get', [/** options **/])`` +:PUT: ``$client->put('http://httpbin.org/put', [/** options **/])`` +:DELETE: ``$client->delete('http://httpbin.org/delete', [/** options **/])`` +:OPTIONS: ``$client->options('http://httpbin.org/get', [/** options **/])`` +:PATCH: ``$client->patch('http://httpbin.org/put', [/** options **/])`` + +.. code-block:: php + + $response = $client->patch('http://httpbin.org/patch', ['body' => 'content']); + +Request URI +----------- + +The resource you are requesting with an HTTP request is identified by the +path of the request, the query string, and the "Host" header of the request. + +When creating a request, you can provide the entire resource URI as a URL. + +.. code-block:: php + + $response = $client->get('http://httbin.org/get?q=foo'); + +Using the above code, you will send a request that uses ``httpbin.org`` as +the Host header, sends the request over port 80, uses ``/get`` as the path, +and sends ``?q=foo`` as the query string. All of this is parsed automatically +from the provided URI. + +Sometimes you don't know what the entire request will be when it is created. +In these cases, you can modify the request as needed before sending it using +the ``createRequest()`` method of the client and methods on the request that +allow you to change it. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + +You can change the path of the request using ``setPath()``: + +.. code-block:: php + + $request->setPath('/get'); + echo $request->getPath(); + // /get + echo $request->getUrl(); + // http://httpbin.com/get + +Scheme +~~~~~~ + +The `scheme `_ of a request +specifies the protocol to use when sending the request. When using Guzzle, the +scheme can be set to "http" or "https". + +You can change the scheme of the request using the ``setScheme()`` method: + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $request->setScheme('https'); + echo $request->getScheme(); + // https + echo $request->getUrl(); + // https://httpbin.com/get + +Port +~~~~ + +No port is necessary when using the "http" or "https" schemes, but you can +override the port using ``setPort()``. If you need to modify the port used with +the specified scheme from the default setting, then you must use the +``setPort()`` method. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $request->setPort(8080); + echo $request->getPort(); + // 8080 + echo $request->getUrl(); + // https://httpbin.com:8080/get + + // Set the port back to the default value for the scheme + $request->setPort(443); + echo $request->getUrl(); + // https://httpbin.com/get + +Query string +~~~~~~~~~~~~ + +You can get the query string of the request using the ``getQuery()`` method. +This method returns a ``GuzzleHttp\Query`` object. A Query object can be +accessed like a PHP array, iterated in a foreach statement like a PHP array, +and cast to a string. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $query = $request->getQuery(); + $query['foo'] = 'bar'; + $query['baz'] = 'bam'; + $query['bam'] = ['test' => 'abc']; + + echo $request->getQuery(); + // foo=bar&baz=bam&bam%5Btest%5D=abc + + echo $request->getQuery()['foo']; + // bar + echo $request->getQuery()->get('foo'); + // bar + echo $request->getQuery()->get('foo'); + // bar + + var_export($request->getQuery()['bam']); + // array('test' => 'abc') + + foreach ($query as $key => $value) { + var_export($value); + } + + echo $request->getUrl(); + // https://httpbin.com/get?foo=bar&baz=bam&bam%5Btest%5D=abc + +Query Aggregators +^^^^^^^^^^^^^^^^^ + +Query objects can store scalar values or arrays of values. When an array of +values is added to a query object, the query object uses a query aggregator to +convert the complex structure into a string. Query objects will use +`PHP style query strings `_ when complex +query string parameters are converted to a string. You can customize how +complex query string parameters are aggregated using the ``setAggregator()`` +method of a query string object. + +.. code-block:: php + + $query->setAggregator($query::duplicateAggregator()); + +In the above example, we've changed the query object to use the +"duplicateAggregator". This aggregator will allow duplicate entries to appear +in a query string rather than appending "[n]" to each value. So if you had a +query string with ``['a' => ['b', 'c']]``, the duplicate aggregator would +convert this to "a=b&a=c" while the default aggregator would convert this to +"a[0]=b&a[1]=c" (with urlencoded brackets). + +The ``setAggregator()`` method accepts a ``callable`` which is used to convert +a deeply nested array of query string variables into a flattened array of key +value pairs. The callable accepts an array of query data and returns a +flattened array of key value pairs where each value is an array of strings. +You can use the ``GuzzleHttp\Query::walkQuery()`` static function to easily +create custom query aggregators. + +Host +~~~~ + +You can change the host header of the request in a predictable way using the +``setHost()`` method of a request: + +.. code-block:: php + + $request->setHost('www.google.com'); + echo $request->getHost(); + // www.google.com + echo $request->getUrl(); + // https://www.google.com/get?foo=bar&baz=bam + +.. note:: + + The Host header can also be changed by modifying the Host header of a + request directly, but modifying the Host header directly could result in + sending a request to a different Host than what is specified in the Host + header (sometimes this is actually the desired behavior). + +Resource +~~~~~~~~ + +You can use the ``getResource()`` method of a request to return the path and +query string of a request in a single string. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org/get?baz=bar'); + echo $request->getResource(); + // /get?baz=bar + +Request Config +-------------- + +Request messages contain a configuration collection that can be used by +event listeners and HTTP handlers to modify how a request behaves or is +transferred over the wire. For example, many of the request options that are +specified when creating a request are actually set as config options that are +only acted upon by handlers and listeners when the request is sent. + +You can get access to the request's config object using the ``getConfig()`` +method of a request. + +.. code-block:: php + + $request = $client->createRequest('GET', '/'); + $config = $request->getConfig(); + +The config object is a ``GuzzleHttp\Collection`` object that acts like +an associative array. You can grab values from the collection using array like +access. You can also modify and remove values using array like access. + +.. code-block:: php + + $config['foo'] = 'bar'; + echo $config['foo']; + // bar + + var_export(isset($config['foo'])); + // true + + unset($config['foo']); + var_export(isset($config['foo'])); + // false + + var_export($config['foo']); + // NULL + +HTTP handlers and event listeners can expose additional customization options +through request config settings. For example, in order to specify custom cURL +options to the cURL handler, you need to specify an associative array in the +``curl`` ``config`` request option. + +.. code-block:: php + + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_HTTPAUTH => CURLAUTH_NTLM, + CURLOPT_USERPWD => 'username:password' + ] + ] + ]); + +Consult the HTTP handlers and event listeners you are using to see if they +allow customization through request configuration options. + +Event Emitter +------------- + +Request objects implement ``GuzzleHttp\Event\HasEmitterInterface``, so they +have a method called ``getEmitter()`` that can be used to get an event emitter +used by the request. Any listener or subscriber attached to a request will only +be triggered for the lifecycle events of a specific request. Conversely, adding +an event listener or subscriber to a client will listen to all lifecycle events +of all requests created by the client. + +See :doc:`events` for more information. + +Responses +========= + +Responses are the HTTP messages a client receives from a server after sending +an HTTP request message. + +Start-Line +---------- + +The start-line of a response contains the protocol and protocol version, +status code, and reason phrase. + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + echo $response->getStatusCode(); + // 200 + echo $response->getReasonPhrase(); + // OK + echo $response->getProtocolVersion(); + // 1.1 + +Body +---- + +As described earlier, you can get the body of a response using the +``getBody()`` method. + +.. code-block:: php + + if ($body = $response->getBody()) { + echo $body; + // Cast to a string: { ... } + $body->seek(0); + // Rewind the body + $body->read(1024); + // Read bytes of the body + } + +When working with JSON responses, you can use the ``json()`` method of a +response: + +.. code-block:: php + + $json = $response->json(); + +.. note:: + + Guzzle uses the ``json_decode()`` method of PHP and uses arrays rather than + ``stdClass`` objects for objects. + +You can use the ``xml()`` method when working with XML data. + +.. code-block:: php + + $xml = $response->xml(); + +.. note:: + + Guzzle uses the ``SimpleXMLElement`` objects when converting response + bodies to XML. + +Effective URL +------------- + +The URL that was ultimately accessed that returned a response can be accessed +using the ``getEffectiveUrl()`` method of a response. This method will return +the URL of a request or the URL of the last redirected URL if any redirects +occurred while transferring a request. + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + echo $response->getEffectiveUrl(); + // http://httpbin.org/get + + $response = GuzzleHttp\get('http://httpbin.org/redirect-to?url=http://www.google.com'); + echo $response->getEffectiveUrl(); + // http://www.google.com diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/index.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/index.rst new file mode 100644 index 00000000..d456a5f0 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/index.rst @@ -0,0 +1,98 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services + +====== +Guzzle +====== + +Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and +trivial to integrate with web services. + +- Manages things like persistent connections, represents query strings as + collections, simplifies sending streaming POST requests with fields and + files, and abstracts away the underlying HTTP transport layer. +- Can send both synchronous and asynchronous requests using the same interface + without requiring a dependency on a specific event loop. +- Pluggable HTTP handlers allows Guzzle to integrate with any method you choose + for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream + wrapper, non-blocking event loops like `React `_, etc.). +- Guzzle makes it so that you no longer need to fool around with cURL options, + stream contexts, or sockets. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $response = $client->get('http://guzzlephp.org'); + $res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); + echo $res->getStatusCode(); + // "200" + echo $res->getHeader('content-type'); + // 'application/json; charset=utf8' + echo $res->getBody(); + // {"type":"User"...' + var_export($res->json()); + // Outputs the JSON decoded data + + // Send an asynchronous request. + $req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]); + $client->send($req)->then(function ($response) { + echo 'I completed! ' . $response; + }); + +User guide +---------- + +.. toctree:: + :maxdepth: 2 + + overview + quickstart + clients + http-messages + events + streams + handlers + testing + faq + +HTTP Components +--------------- + +There are a number of optional libraries you can use along with Guzzle's HTTP +layer to add capabilities to the client. + +`Log Subscriber `_ + Logs HTTP requests and responses sent over the wire using customizable + log message templates. + +`OAuth Subscriber `_ + Signs requests using OAuth 1.0. + +`Cache Subscriber `_ + Implements a private transparent proxy cache that caches HTTP responses. + +`Retry Subscriber `_ + Retries failed requests using customizable retry strategies (e.g., retry + based on response status code, cURL error codes, etc.) + +`Message Integrity Subscriber `_ + Verifies the message integrity of HTTP responses using customizable + validators. This plugin can be used, for example, to verify the Content-MD5 + headers of responses. + +Service Description Commands +---------------------------- + +You can use the **Guzzle Command** library to encapsulate interaction with a +web service using command objects. Building on top of Guzzle's command +abstraction allows you to easily implement things like service description that +can be used to serialize requests and parse responses using a meta-description +of a web service. + +`Guzzle Command `_ + Provides the foundational elements used to build high-level, command based, + web service clients with Guzzle. + +`Guzzle Services `_ + Provides an implementation of the *Guzzle Command* library that uses + Guzzle service descriptions to describe web services, serialize requests, + and parse responses into easy to use model structures. diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/overview.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/overview.rst new file mode 100644 index 00000000..1355afa1 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/overview.rst @@ -0,0 +1,150 @@ +======== +Overview +======== + +Requirements +============ + +#. PHP 5.4.0 +#. To use the PHP stream handler, ``allow_url_fopen`` must be enabled in your + system's php.ini. +#. To use the cURL handler, you must have a recent version of cURL >= 7.16.2 + compiled with OpenSSL and zlib. + +.. note:: + + Guzzle no longer requires cURL in order to send HTTP requests. Guzzle will + use the PHP stream wrapper to send HTTP requests if cURL is not installed. + Alternatively, you can provide your own HTTP handler used to send requests. + +.. _installation: + +Installation +============ + +The recommended way to install Guzzle is with `Composer `_. Composer is a dependency +management tool for PHP that allows you to declare the dependencies your project needs and installs them into your +project. + +.. code-block:: bash + + # Install Composer + curl -sS https://getcomposer.org/installer | php + +You can add Guzzle as a dependency using the composer.phar CLI: + +.. code-block:: bash + + php composer.phar require guzzlehttp/guzzle:~5.0 + +Alternatively, you can specify Guzzle as a dependency in your project's +existing composer.json file: + +.. code-block:: js + + { + "require": { + "guzzlehttp/guzzle": "~5.0" + } + } + +After installing, you need to require Composer's autoloader: + +.. code-block:: php + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and +other best-practices for defining dependencies at `getcomposer.org `_. + +Bleeding edge +------------- + +During your development, you can keep up with the latest changes on the master +branch by setting the version requirement for Guzzle to ``~5.0@dev``. + +.. code-block:: js + + { + "require": { + "guzzlehttp/guzzle": "~5.0@dev" + } + } + +License +======= + +Licensed using the `MIT license `_. + + Copyright (c) 2014 Michael Dowling + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Contributing +============ + +Guidelines +---------- + +1. Guzzle follows PSR-0, PSR-1, and PSR-2. +2. Guzzle is meant to be lean and fast with very few dependencies. +3. Guzzle has a minimum PHP version requirement of PHP 5.4. Pull requests must + not require a PHP version greater than PHP 5.4. +4. All pull requests must include unit tests to ensure the change works as + expected and to prevent regressions. + +Running the tests +----------------- + +In order to contribute, you'll need to checkout the source from GitHub and +install Guzzle's dependencies using Composer: + +.. code-block:: bash + + git clone https://github.com/guzzle/guzzle.git + cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev + +Guzzle is unit tested with PHPUnit. Run the tests using the vendored PHPUnit +binary: + +.. code-block:: bash + + vendor/bin/phpunit + +.. note:: + + You'll need to install node.js v0.5.0 or newer in order to perform + integration tests on Guzzle's HTTP handlers. + +Reporting a security vulnerability +================================== + +We want to ensure that Guzzle is a secure HTTP client library for everyone. If +you've discovered a security vulnerability in Guzzle, we appreciate your help +in disclosing it to us in a `responsible manner `_. + +Publicly disclosing a vulnerability can put the entire community at risk. If +you've discovered a security concern, please email us at +security@guzzlephp.org. We'll work with you to make sure that we understand the +scope of the issue, and that we fully address your concern. We consider +correspondence sent to security@guzzlephp.org our highest priority, and work to +address any issues that arise as quickly as possible. + +After a security vulnerability has been corrected, a security hotfix release will +be deployed as soon as possible. diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/quickstart.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/quickstart.rst new file mode 100644 index 00000000..65a70ed2 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/quickstart.rst @@ -0,0 +1,448 @@ +========== +Quickstart +========== + +This page provides a quick introduction to Guzzle and introductory examples. +If you have not already installed, Guzzle, head over to the :ref:`installation` +page. + +Make a Request +============== + +You can send requests with Guzzle using a ``GuzzleHttp\ClientInterface`` +object. + +Creating a Client +----------------- + +The procedural API is simple but not very testable; it's best left for quick +prototyping. If you want to use Guzzle in a more flexible and testable way, +then you'll need to use a ``GuzzleHttp\ClientInterface`` object. + +.. code-block:: php + + use GuzzleHttp\Client; + + $client = new Client(); + $response = $client->get('http://httpbin.org/get'); + + // You can use the same methods you saw in the procedural API + $response = $client->delete('http://httpbin.org/delete'); + $response = $client->head('http://httpbin.org/get'); + $response = $client->options('http://httpbin.org/get'); + $response = $client->patch('http://httpbin.org/patch'); + $response = $client->post('http://httpbin.org/post'); + $response = $client->put('http://httpbin.org/put'); + +You can create a request with a client and then send the request with the +client when you're ready. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://www.foo.com'); + $response = $client->send($request); + +Client objects provide a great deal of flexibility in how request are +transferred including default request options, subscribers that are attached +to each request, and a base URL that allows you to send requests with relative +URLs. You can find out all about clients in the :doc:`clients` page of the +documentation. + +Using Responses +=============== + +In the previous examples, we retrieved a ``$response`` variable. This value is +actually a ``GuzzleHttp\Message\ResponseInterface`` object and contains lots +of helpful information. + +You can get the status code and reason phrase of the response. + +.. code-block:: php + + $code = $response->getStatusCode(); + // 200 + + $reason = $response->getReasonPhrase(); + // OK + +By providing the ``future`` request option to a request, you can send requests +asynchronously using the promise interface of a future response. + +.. code-block:: php + + $client->get('http://httpbin.org', ['future' => true]) + ->then(function ($response) { + echo $response->getStatusCode(); + }); + +Response Body +------------- + +The body of a response can be retrieved and cast to a string. + +.. code-block:: php + + $body = $response->getBody(); + echo $body; + // { "some_json_data" ...} + +You can also read read bytes from body of a response like a stream. + +.. code-block:: php + + $body = $response->getBody(); + + while (!$body->eof()) { + echo $body->read(1024); + } + +JSON Responses +~~~~~~~~~~~~~~ + +You can more easily work with JSON responses using the ``json()`` method of a +response. + +.. code-block:: php + + $response = $client->get('http://httpbin.org/get'); + $json = $response->json(); + var_dump($json[0]['origin']); + +Guzzle internally uses PHP's ``json_decode()`` function to parse responses. If +Guzzle is unable to parse the JSON response body, then a +``GuzzleHttp\Exception\ParseException`` is thrown. + +XML Responses +~~~~~~~~~~~~~ + +You can use a response's ``xml()`` method to more easily work with responses +that contain XML data. + +.. code-block:: php + + $response = $client->get('https://github.com/mtdowling.atom'); + $xml = $response->xml(); + echo $xml->id; + // tag:github.com,2008:/mtdowling + +Guzzle internally uses a ``SimpleXMLElement`` object to parse responses. If +Guzzle is unable to parse the XML response body, then a +``GuzzleHttp\Exception\ParseException`` is thrown. + +Query String Parameters +======================= + +Sending query string parameters with a request is easy. You can set query +string parameters in the request's URL. + +.. code-block:: php + + $response = $client->get('http://httpbin.org?foo=bar'); + +You can also specify the query string parameters using the ``query`` request +option. + +.. code-block:: php + + $client->get('http://httpbin.org', [ + 'query' => ['foo' => 'bar'] + ]); + +And finally, you can build up the query string of a request as needed by +calling the ``getQuery()`` method of a request and modifying the request's +``GuzzleHttp\Query`` object as needed. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org'); + $query = $request->getQuery(); + $query->set('foo', 'bar'); + + // You can use the query string object like an array + $query['baz'] = 'bam'; + + // The query object can be cast to a string + echo $query; + // foo=bar&baz=bam + + // Setting a value to false or null will cause the "=" sign to be omitted + $query['empty'] = null; + echo $query; + // foo=bar&baz=bam&empty + + // Use an empty string to include the "=" sign with an empty value + $query['empty'] = ''; + echo $query; + // foo=bar&baz=bam&empty= + +.. _headers: + +Request and Response Headers +---------------------------- + +You can specify request headers when sending or creating requests with a +client. In the following example, we send the ``X-Foo-Header`` with a value of +``value`` by setting the ``headers`` request option. + +.. code-block:: php + + $response = $client->get('http://httpbin.org/get', [ + 'headers' => ['X-Foo-Header' => 'value'] + ]); + +You can view the headers of a response using header specific methods of a +response class. Headers work exactly the same way for request and response +object. + +You can retrieve a header from a request or response using the ``getHeader()`` +method of the object. This method is case-insensitive and by default will +return a string containing the header field value. + +.. code-block:: php + + $response = $client->get('http://www.yahoo.com'); + $length = $response->getHeader('Content-Length'); + +Header fields that contain multiple values can be retrieved as a string or as +an array. Retrieving the field values as a string will naively concatenate all +of the header values together with a comma. Because not all header fields +should be represented this way (e.g., ``Set-Cookie``), you can pass an optional +flag to the ``getHeader()`` method to retrieve the header values as an array. + +.. code-block:: php + + $values = $response->getHeader('Set-Cookie', true); + foreach ($values as $value) { + echo $value; + } + +You can test if a request or response has a specific header using the +``hasHeader()`` method. This method accepts a case-insensitive string and +returns true if the header is present or false if it is not. + +You can retrieve all of the headers of a message using the ``getHeaders()`` +method of a request or response. The return value is an associative array where +the keys represent the header name as it will be sent over the wire, and each +value is an array of strings associated with the header. + +.. code-block:: php + + $headers = $response->getHeaders(); + foreach ($message->getHeaders() as $name => $values) { + echo $name . ": " . implode(", ", $values); + } + +Modifying headers +----------------- + +The headers of a message can be modified using the ``setHeader()``, +``addHeader()``, ``setHeaders()``, and ``removeHeader()`` methods of a request +or response object. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org/get'); + + // Set a single value for a header + $request->setHeader('User-Agent', 'Testing!'); + + // Set multiple values for a header in one call + $request->setHeader('X-Foo', ['Baz', 'Bar']); + + // Add a header to the message + $request->addHeader('X-Foo', 'Bam'); + + echo $request->getHeader('X-Foo'); + // Baz, Bar, Bam + + // Remove a specific header using a case-insensitive name + $request->removeHeader('x-foo'); + echo $request->getHeader('X-Foo'); + // Echoes an empty string: '' + +Uploading Data +============== + +Guzzle provides several methods of uploading data. + +You can send requests that contain a stream of data by passing a string, +resource returned from ``fopen``, or a ``GuzzleHttp\Stream\StreamInterface`` +object to the ``body`` request option. + +.. code-block:: php + + $r = $client->post('http://httpbin.org/post', ['body' => 'raw data']); + +You can easily upload JSON data using the ``json`` request option. + +.. code-block:: php + + $r = $client->put('http://httpbin.org/put', ['json' => ['foo' => 'bar']]); + +POST Requests +------------- + +In addition to specifying the raw data of a request using the ``body`` request +option, Guzzle provides helpful abstractions over sending POST data. + +Sending POST Fields +~~~~~~~~~~~~~~~~~~~ + +Sending ``application/x-www-form-urlencoded`` POST requests requires that you +specify the body of a POST request as an array. + +.. code-block:: php + + $response = $client->post('http://httpbin.org/post', [ + 'body' => [ + 'field_name' => 'abc', + 'other_field' => '123' + ] + ]); + +You can also build up POST requests before sending them. + +.. code-block:: php + + $request = $client->createRequest('POST', 'http://httpbin.org/post'); + $postBody = $request->getBody(); + + // $postBody is an instance of GuzzleHttp\Post\PostBodyInterface + $postBody->setField('foo', 'bar'); + echo $postBody->getField('foo'); + // 'bar' + + echo json_encode($postBody->getFields()); + // {"foo": "bar"} + + // Send the POST request + $response = $client->send($request); + +Sending POST Files +~~~~~~~~~~~~~~~~~~ + +Sending ``multipart/form-data`` POST requests (POST requests that contain +files) is the same as sending ``application/x-www-form-urlencoded``, except +some of the array values of the POST fields map to PHP ``fopen`` resources, or +``GuzzleHttp\Stream\StreamInterface``, or +``GuzzleHttp\Post\PostFileInterface`` objects. + +.. code-block:: php + + use GuzzleHttp\Post\PostFile; + + $response = $client->post('http://httpbin.org/post', [ + 'body' => [ + 'field_name' => 'abc', + 'file_filed' => fopen('/path/to/file', 'r'), + 'other_file' => new PostFile('other_file', 'this is the content') + ] + ]); + +Just like when sending POST fields, you can also build up POST requests with +files before sending them. + +.. code-block:: php + + use GuzzleHttp\Post\PostFile; + + $request = $client->createRequest('POST', 'http://httpbin.org/post'); + $postBody = $request->getBody(); + $postBody->setField('foo', 'bar'); + $postBody->addFile(new PostFile('test', fopen('/path/to/file', 'r'))); + $response = $client->send($request); + +Cookies +======= + +Guzzle can maintain a cookie session for you if instructed using the +``cookies`` request option. + +- Set to ``true`` to use a shared cookie session associated with the client. +- Pass an associative array containing cookies to send in the request and start + a new cookie session. +- Set to a ``GuzzleHttp\Subscriber\CookieJar\CookieJarInterface`` object to use + an existing cookie jar. + +Redirects +========= + +Guzzle will automatically follow redirects unless you tell it not to. You can +customize the redirect behavior using the ``allow_redirects`` request option. + +- Set to true to enable normal redirects with a maximum number of 5 redirects. + This is the default setting. +- Set to false to disable redirects. +- Pass an associative array containing the 'max' key to specify the maximum + number of redirects and optionally provide a 'strict' key value to specify + whether or not to use strict RFC compliant redirects (meaning redirect POST + requests with POST requests vs. doing what most browsers do which is + redirect POST requests with GET requests). + +.. code-block:: php + + $response = $client->get('http://github.com'); + echo $response->getStatusCode(); + // 200 + echo $response->getEffectiveUrl(); + // 'https://github.com/' + +The following example shows that redirects can be disabled. + +.. code-block:: php + + $response = $client->get('http://github.com', ['allow_redirects' => false]); + echo $response->getStatusCode(); + // 301 + echo $response->getEffectiveUrl(); + // 'http://github.com/' + +Exceptions +========== + +Guzzle throws exceptions for errors that occur during a transfer. + +- In the event of a networking error (connection timeout, DNS errors, etc.), + a ``GuzzleHttp\Exception\RequestException`` is thrown. This exception + extends from ``GuzzleHttp\Exception\TransferException``. Catching this + exception will catch any exception that can be thrown while transferring + (non-parallel) requests. + + .. code-block:: php + + use GuzzleHttp\Exception\RequestException; + + try { + $client->get('https://github.com/_abc_123_404'); + } catch (RequestException $e) { + echo $e->getRequest(); + if ($e->hasResponse()) { + echo $e->getResponse(); + } + } + +- A ``GuzzleHttp\Exception\ClientException`` is thrown for 400 + level errors if the ``exceptions`` request option is set to true. This + exception extends from ``GuzzleHttp\Exception\BadResponseException`` and + ``GuzzleHttp\Exception\BadResponseException`` extends from + ``GuzzleHttp\Exception\RequestException``. + + .. code-block:: php + + use GuzzleHttp\Exception\ClientException; + + try { + $client->get('https://github.com/_abc_123_404'); + } catch (ClientException $e) { + echo $e->getRequest(); + echo $e->getResponse(); + } + +- A ``GuzzleHttp\Exception\ServerException`` is thrown for 500 level + errors if the ``exceptions`` request option is set to true. This + exception extends from ``GuzzleHttp\Exception\BadResponseException``. +- A ``GuzzleHttp\Exception\TooManyRedirectsException`` is thrown when too + many redirects are followed. This exception extends from ``GuzzleHttp\Exception\RequestException``. + +All of the above exceptions extend from +``GuzzleHttp\Exception\TransferException``. diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/requirements.txt b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/requirements.txt new file mode 100644 index 00000000..fe7a4eab --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/requirements.txt @@ -0,0 +1,2 @@ +Sphinx>=1.2b1 +guzzle_sphinx_theme>=0.6.0 diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/streams.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/streams.rst new file mode 100644 index 00000000..8fe9a698 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/streams.rst @@ -0,0 +1,213 @@ +======= +Streams +======= + +Guzzle uses stream objects to represent request and response message bodies. +These stream objects allow you to work with various types of data all using a +common interface. + +HTTP messages consist of a start-line, headers, and a body. The body of an HTTP +message can be very small or extremely large. Attempting to represent the body +of a message as a string can easily consume more memory than intended because +the body must be stored completely in memory. Attempting to store the body of a +request or response in memory would preclude the use of that implementation from +being able to work with large message bodies. The StreamInterface is used in +order to hide the implementation details of where a stream of data is read from +or written to. + +Guzzle's StreamInterface exposes several methods that enable streams to be read +from, written to, and traversed effectively. + +Streams expose their capabilities using three methods: ``isReadable()``, +``isWritable()``, and ``isSeekable()``. These methods can be used by stream +collaborators to determine if a stream is capable of their requirements. + +Each stream instance has various capabilities: they can be read-only, +write-only, read-write, allow arbitrary random access (seeking forwards or +backwards to any location), or only allow sequential access (for example in the +case of a socket or pipe). + +Creating Streams +================ + +The best way to create a stream is using the static factory method, +``GuzzleHttp\Stream\Stream::factory()``. This factory accepts strings, +resources returned from ``fopen()``, an object that implements +``__toString()``, and an object that implements +``GuzzleHttp\Stream\StreamInterface``. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + + $stream = Stream::factory('string data'); + echo $stream; + // string data + echo $stream->read(3); + // str + echo $stream->getContents(); + // ing data + var_export($stream->eof()); + // true + var_export($stream->tell()); + // 11 + +Metadata +======== + +Guzzle streams expose stream metadata through the ``getMetadata()`` method. +This method provides the data you would retrieve when calling PHP's +`stream_get_meta_data() function `_, +and can optionally expose other custom data. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + + $resource = fopen('/path/to/file', 'r'); + $stream = Stream::factory($resource); + echo $stream->getMetadata('uri'); + // /path/to/file + var_export($stream->isReadable()); + // true + var_export($stream->isWritable()); + // false + var_export($stream->isSeekable()); + // true + +Stream Decorators +================= + +With the small and focused interface, add custom functionality to streams is +very simple with stream decorators. Guzzle provides several built-in decorators +that provide additional stream functionality. + +CachingStream +------------- + +The CachingStream is used to allow seeking over previously read bytes on +non-seekable streams. This can be useful when transferring a non-seekable +entity body fails due to needing to rewind the stream (for example, resulting +from a redirect). Data that is read from the remote stream will be buffered in +a PHP temp stream so that previously read bytes are cached first in memory, +then on disk. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + use GuzzleHttp\Stream\CachingStream; + + $original = Stream::factory(fopen('http://www.google.com', 'r')); + $stream = new CachingStream($original); + + $stream->read(1024); + echo $stream->tell(); + // 1024 + + $stream->seek(0); + echo $stream->tell(); + // 0 + +LimitStream +----------- + +LimitStream can be used to read a subset or slice of an existing stream object. +This can be useful for breaking a large file into smaller pieces to be sent in +chunks (e.g. Amazon S3's multipart upload API). + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + use GuzzleHttp\Stream\LimitStream; + + $original = Stream::factory(fopen('/tmp/test.txt', 'r+')); + echo $original->getSize(); + // >>> 1048576 + + // Limit the size of the body to 1024 bytes and start reading from byte 2048 + $stream = new LimitStream($original, 1024, 2048); + echo $stream->getSize(); + // >>> 1024 + echo $stream->tell(); + // >>> 0 + +NoSeekStream +------------ + +NoSeekStream wraps a stream and does not allow seeking. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + use GuzzleHttp\Stream\LimitStream; + + $original = Stream::factory('foo'); + $noSeek = new NoSeekStream($original); + + echo $noSeek->read(3); + // foo + var_export($noSeek->isSeekable()); + // false + $noSeek->seek(0); + var_export($noSeek->read(3)); + // NULL + +Creating Custom Decorators +-------------------------- + +Creating a stream decorator is very easy thanks to the +``GuzzleHttp\Stream\StreamDecoratorTrait``. This trait provides methods that +implement ``GuzzleHttp\Stream\StreamInterface`` by proxying to an underlying +stream. Just ``use`` the ``StreamDecoratorTrait`` and implement your custom +methods. + +For example, let's say we wanted to call a specific function each time the last +byte is read from a stream. This could be implemented by overriding the +``read()`` method. + +.. code-block:: php + + use GuzzleHttp\Stream\StreamDecoratorTrait; + + class EofCallbackStream implements StreamInterface + { + use StreamDecoratorTrait; + + private $callback; + + public function __construct(StreamInterface $stream, callable $callback) + { + $this->stream = $stream; + $this->callback = $callback; + } + + public function read($length) + { + $result = $this->stream->read($length); + + // Invoke the callback when EOF is hit. + if ($this->eof()) { + call_user_func($this->callback); + } + + return $result; + } + } + +This decorator could be added to any existing stream and used like so: + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + + $original = Stream::factory('foo'); + $eofStream = new EofCallbackStream($original, function () { + echo 'EOF!'; + }); + + $eofStream->read(2); + $eofStream->read(1); + // echoes "EOF!" + $eofStream->seek(0); + $eofStream->read(3); + // echoes "EOF!" diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/testing.rst b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/testing.rst new file mode 100644 index 00000000..03bcc2ee --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/docs/testing.rst @@ -0,0 +1,232 @@ +====================== +Testing Guzzle Clients +====================== + +Guzzle provides several tools that will enable you to easily mock the HTTP +layer without needing to send requests over the internet. + +* Mock subscriber +* Mock handler +* Node.js web server for integration testing + +Mock Subscriber +=============== + +When testing HTTP clients, you often need to simulate specific scenarios like +returning a successful response, returning an error, or returning specific +responses in a certain order. Because unit tests need to be predictable, easy +to bootstrap, and fast, hitting an actual remote API is a test smell. + +Guzzle provides a mock subscriber that can be attached to clients or requests +that allows you to queue up a list of responses to use rather than hitting a +remote API. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Subscriber\Mock; + use GuzzleHttp\Message\Response; + + $client = new Client(); + + // Create a mock subscriber and queue two responses. + $mock = new Mock([ + new Response(200, ['X-Foo' => 'Bar']), // Use response object + "HTTP/1.1 202 OK\r\nContent-Length: 0\r\n\r\n" // Use a response string + ]); + + // Add the mock subscriber to the client. + $client->getEmitter()->attach($mock); + // The first request is intercepted with the first response. + echo $client->get('/')->getStatusCode(); + //> 200 + // The second request is intercepted with the second response. + echo $client->get('/')->getStatusCode(); + //> 202 + +When no more responses are in the queue and a request is sent, an +``OutOfBoundsException`` is thrown. + +History Subscriber +================== + +When using things like the ``Mock`` subscriber, you often need to know if the +requests you expected to send were sent exactly as you intended. While the mock +subscriber responds with mocked responses, the ``GuzzleHttp\Subscriber\History`` +subscriber maintains a history of the requests that were sent by a client. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Subscriber\History; + + $client = new Client(); + $history = new History(); + + // Add the history subscriber to the client. + $client->getEmitter()->attach($history); + + $client->get('http://httpbin.org/get'); + $client->head('http://httpbin.org/get'); + + // Count the number of transactions + echo count($history); + //> 2 + // Get the last request + $lastRequest = $history->getLastRequest(); + // Get the last response + $lastResponse = $history->getLastResponse(); + + // Iterate over the transactions that were sent + foreach ($history as $transaction) { + echo $transaction['request']->getMethod(); + //> GET, HEAD + echo $transaction['response']->getStatusCode(); + //> 200, 200 + } + +The history subscriber can also be printed, revealing the requests and +responses that were sent as a string, in order. + +.. code-block:: php + + echo $history; + +:: + + > GET /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 + + < HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Content-Type: application/json + Date: Tue, 25 Mar 2014 03:53:27 GMT + Server: gunicorn/0.17.4 + Content-Length: 270 + Connection: keep-alive + + { + "headers": { + "Connection": "close", + "X-Request-Id": "3d0f7d5c-c937-4394-8248-2b8e03fcccdb", + "User-Agent": "Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8", + "Host": "httpbin.org" + }, + "origin": "76.104.247.1", + "args": {}, + "url": "http://httpbin.org/get" + } + + > HEAD /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 + + < HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Content-length: 270 + Content-Type: application/json + Date: Tue, 25 Mar 2014 03:53:27 GMT + Server: gunicorn/0.17.4 + Connection: keep-alive + +Mock Adapter +============ + +In addition to using the Mock subscriber, you can use the +``GuzzleHttp\Ring\Client\MockHandler`` as the handler of a client to return the +same response over and over or return the result of a callable function. + +Test Web Server +=============== + +Using mock responses is almost always enough when testing a web service client. +When implementing custom :doc:`HTTP handlers `, you'll need to send +actual HTTP requests in order to sufficiently test the handler. However, a +best practice is to contact a local web server rather than a server over the +internet. + +- Tests are more reliable +- Tests do not require a network connection +- Tests have no external dependencies + +Using the test server +--------------------- + +.. warning:: + + The following functionality is provided to help developers of Guzzle + develop HTTP handlers. There is no promise of backwards compatibility + when it comes to the node.js test server or the ``GuzzleHttp\Tests\Server`` + class. If you are using the test server or ``Server`` class outside of + guzzlehttp/guzzle, then you will need to configure autoloading and + ensure the web server is started manually. + +.. hint:: + + You almost never need to use this test web server. You should only ever + consider using it when developing HTTP handlers. The test web server + is not necessary for mocking requests. For that, please use the + Mock subcribers and History subscriber. + +Guzzle ships with a node.js test server that receives requests and returns +responses from a queue. The test server exposes a simple API that is used to +enqueue responses and inspect the requests that it has received. + +Any operation on the ``Server`` object will ensure that +the server is running and wait until it is able to receive requests before +returning. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Tests\Server; + + // Start the server and queue a response + Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); + + $client = new Client(['base_url' => Server::$url]); + echo $client->get('/foo')->getStatusCode(); + // 200 + +``GuzzleHttp\Tests\Server`` provides a static interface to the test server. You +can queue an HTTP response or an array of responses by calling +``Server::enqueue()``. This method accepts a string representing an HTTP +response message, a ``GuzzleHttp\Message\ResponseInterface``, or an array of +HTTP message strings / ``GuzzleHttp\Message\ResponseInterface`` objects. + +.. code-block:: php + + // Queue single response + Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); + + // Clear the queue and queue an array of responses + Server::enqueue([ + "HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n", + "HTTP/1.1 404 Not Found\r\n\Content-Length: 0r\n\r\n" + ]); + +When a response is queued on the test server, the test server will remove any +previously queued responses. As the server receives requests, queued responses +are dequeued and returned to the request. When the queue is empty, the server +will return a 500 response. + +You can inspect the requests that the server has retrieved by calling +``Server::received()``. This method accepts an optional ``$hydrate`` parameter +that specifies if you are retrieving an array of HTTP requests as strings or an +array of ``GuzzleHttp\Message\RequestInterface`` objects. + +.. code-block:: php + + foreach (Server::received() as $response) { + echo $response; + } + +You can clear the list of received requests from the web server using the +``Server::flush()`` method. + +.. code-block:: php + + Server::flush(); + echo count(Server::received()); + // 0 diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/phpunit.xml.dist b/lib/Ebanx/vendor/guzzlehttp/guzzle/phpunit.xml.dist new file mode 100644 index 00000000..500cd53a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + + tests + + + + + src + + src/ + + + + diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/BatchResults.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/BatchResults.php new file mode 100644 index 00000000..e5af433d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/BatchResults.php @@ -0,0 +1,148 @@ +hash = $hash; + } + + /** + * Get the keys that are available on the batch result. + * + * @return array + */ + public function getKeys() + { + return iterator_to_array($this->hash); + } + + /** + * Gets a result from the container for the given object. When getting + * results for a batch of requests, provide the request object. + * + * @param object $forObject Object to retrieve the result for. + * + * @return mixed|null + */ + public function getResult($forObject) + { + return isset($this->hash[$forObject]) ? $this->hash[$forObject] : null; + } + + /** + * Get an array of successful results. + * + * @return array + */ + public function getSuccessful() + { + $results = []; + foreach ($this->hash as $key) { + if (!($this->hash[$key] instanceof \Exception)) { + $results[] = $this->hash[$key]; + } + } + + return $results; + } + + /** + * Get an array of failed results. + * + * @return array + */ + public function getFailures() + { + $results = []; + foreach ($this->hash as $key) { + if ($this->hash[$key] instanceof \Exception) { + $results[] = $this->hash[$key]; + } + } + + return $results; + } + + /** + * Allows iteration over all batch result values. + * + * @return \ArrayIterator + */ + public function getIterator() + { + $results = []; + foreach ($this->hash as $key) { + $results[] = $this->hash[$key]; + } + + return new \ArrayIterator($results); + } + + /** + * Counts the number of elements in the batch result. + * + * @return int + */ + public function count() + { + return count($this->hash); + } + + /** + * Checks if the batch contains a specific numerical array index. + * + * @param int $key Index to access + * + * @return bool + */ + public function offsetExists($key) + { + return $key < count($this->hash); + } + + /** + * Allows access of the batch using a numerical array index. + * + * @param int $key Index to access. + * + * @return mixed|null + */ + public function offsetGet($key) + { + $i = -1; + foreach ($this->hash as $obj) { + if ($key === ++$i) { + return $this->hash[$obj]; + } + } + + return null; + } + + public function offsetUnset($key) + { + throw new \RuntimeException('Not implemented'); + } + + public function offsetSet($key, $value) + { + throw new \RuntimeException('Not implemented'); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Client.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Client.php new file mode 100644 index 00000000..cf5dd469 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Client.php @@ -0,0 +1,362 @@ + [ + * 'http://www.foo.com/{version}/', + * ['version' => '123'] + * ], + * 'defaults' => [ + * 'timeout' => 10, + * 'allow_redirects' => false, + * 'proxy' => '192.168.16.1:10' + * ] + * ]); + * + * @param array $config Client configuration settings + * - base_url: Base URL of the client that is merged into relative URLs. + * Can be a string or an array that contains a URI template followed + * by an associative array of expansion variables to inject into the + * URI template. + * - handler: callable RingPHP handler used to transfer requests + * - message_factory: Factory used to create request and response object + * - defaults: Default request options to apply to each request + * - emitter: Event emitter used for request events + * - fsm: (internal use only) The request finite state machine. A + * function that accepts a transaction and optional final state. The + * function is responsible for transitioning a request through its + * lifecycle events. + */ + public function __construct(array $config = []) + { + $this->configureBaseUrl($config); + $this->configureDefaults($config); + + if (isset($config['emitter'])) { + $this->emitter = $config['emitter']; + } + + $this->messageFactory = isset($config['message_factory']) + ? $config['message_factory'] + : new MessageFactory(); + + if (isset($config['fsm'])) { + $this->fsm = $config['fsm']; + } else { + if (isset($config['handler'])) { + $handler = $config['handler']; + } elseif (isset($config['adapter'])) { + $handler = $config['adapter']; + } else { + $handler = Utils::getDefaultHandler(); + } + $this->fsm = new RequestFsm($handler, $this->messageFactory); + } + } + + public function getDefaultOption($keyOrPath = null) + { + return $keyOrPath === null + ? $this->defaults + : Utils::getPath($this->defaults, $keyOrPath); + } + + public function setDefaultOption($keyOrPath, $value) + { + Utils::setPath($this->defaults, $keyOrPath, $value); + } + + public function getBaseUrl() + { + return (string) $this->baseUrl; + } + + public function createRequest($method, $url = null, array $options = []) + { + $options = $this->mergeDefaults($options); + // Use a clone of the client's emitter + $options['config']['emitter'] = clone $this->getEmitter(); + $url = $url || (is_string($url) && strlen($url)) + ? $this->buildUrl($url) + : (string) $this->baseUrl; + + return $this->messageFactory->createRequest($method, $url, $options); + } + + public function get($url = null, $options = []) + { + return $this->send($this->createRequest('GET', $url, $options)); + } + + public function head($url = null, array $options = []) + { + return $this->send($this->createRequest('HEAD', $url, $options)); + } + + public function delete($url = null, array $options = []) + { + return $this->send($this->createRequest('DELETE', $url, $options)); + } + + public function put($url = null, array $options = []) + { + return $this->send($this->createRequest('PUT', $url, $options)); + } + + public function patch($url = null, array $options = []) + { + return $this->send($this->createRequest('PATCH', $url, $options)); + } + + public function post($url = null, array $options = []) + { + return $this->send($this->createRequest('POST', $url, $options)); + } + + public function options($url = null, array $options = []) + { + return $this->send($this->createRequest('OPTIONS', $url, $options)); + } + + public function send(RequestInterface $request) + { + $isFuture = $request->getConfig()->get('future'); + $trans = new Transaction($this, $request, $isFuture); + $fn = $this->fsm; + + try { + $fn($trans); + if ($isFuture) { + // Turn the normal response into a future if needed. + return $trans->response instanceof FutureInterface + ? $trans->response + : new FutureResponse(new FulfilledPromise($trans->response)); + } + // Resolve deep futures if this is not a future + // transaction. This accounts for things like retries + // that do not have an immediate side-effect. + while ($trans->response instanceof FutureInterface) { + $trans->response = $trans->response->wait(); + } + return $trans->response; + } catch (\Exception $e) { + if ($isFuture) { + // Wrap the exception in a promise + return new FutureResponse(new RejectedPromise($e)); + } + throw RequestException::wrapException($trans->request, $e); + } catch (\TypeError $error) { + $exception = new \Exception($error->getMessage(), $error->getCode(), $error); + if ($isFuture) { + // Wrap the exception in a promise + return new FutureResponse(new RejectedPromise($exception)); + } + throw RequestException::wrapException($trans->request, $exception); + } + } + + /** + * Get an array of default options to apply to the client + * + * @return array + */ + protected function getDefaultOptions() + { + $settings = [ + 'allow_redirects' => true, + 'exceptions' => true, + 'decode_content' => true, + 'verify' => true + ]; + + // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. + // We can only trust the HTTP_PROXY environment variable in a CLI + // process due to the fact that PHP has no reliable mechanism to + // get environment variables that start with "HTTP_". + if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) { + $settings['proxy']['http'] = getenv('HTTP_PROXY'); + } + + if ($proxy = getenv('HTTPS_PROXY')) { + $settings['proxy']['https'] = $proxy; + } + + return $settings; + } + + /** + * Expand a URI template and inherit from the base URL if it's relative + * + * @param string|array $url URL or an array of the URI template to expand + * followed by a hash of template varnames. + * @return string + * @throws \InvalidArgumentException + */ + private function buildUrl($url) + { + // URI template (absolute or relative) + if (!is_array($url)) { + return strpos($url, '://') + ? (string) $url + : (string) $this->baseUrl->combine($url); + } + + if (!isset($url[1])) { + throw new \InvalidArgumentException('You must provide a hash of ' + . 'varname options in the second element of a URL array.'); + } + + // Absolute URL + if (strpos($url[0], '://')) { + return Utils::uriTemplate($url[0], $url[1]); + } + + // Combine the relative URL with the base URL + return (string) $this->baseUrl->combine( + Utils::uriTemplate($url[0], $url[1]) + ); + } + + private function configureBaseUrl(&$config) + { + if (!isset($config['base_url'])) { + $this->baseUrl = new Url('', ''); + } elseif (!is_array($config['base_url'])) { + $this->baseUrl = Url::fromString($config['base_url']); + } elseif (count($config['base_url']) < 2) { + throw new \InvalidArgumentException('You must provide a hash of ' + . 'varname options in the second element of a base_url array.'); + } else { + $this->baseUrl = Url::fromString( + Utils::uriTemplate( + $config['base_url'][0], + $config['base_url'][1] + ) + ); + $config['base_url'] = (string) $this->baseUrl; + } + } + + private function configureDefaults($config) + { + if (!isset($config['defaults'])) { + $this->defaults = $this->getDefaultOptions(); + } else { + $this->defaults = array_replace( + $this->getDefaultOptions(), + $config['defaults'] + ); + } + + // Add the default user-agent header + if (!isset($this->defaults['headers'])) { + $this->defaults['headers'] = [ + 'User-Agent' => Utils::getDefaultUserAgent() + ]; + } elseif (!Core::hasHeader($this->defaults, 'User-Agent')) { + // Add the User-Agent header if one was not already set + $this->defaults['headers']['User-Agent'] = Utils::getDefaultUserAgent(); + } + } + + /** + * Merges default options into the array passed by reference. + * + * @param array $options Options to modify by reference + * + * @return array + */ + private function mergeDefaults($options) + { + $defaults = $this->defaults; + + // Case-insensitively merge in default headers if both defaults and + // options have headers specified. + if (!empty($defaults['headers']) && !empty($options['headers'])) { + // Create a set of lowercased keys that are present. + $lkeys = []; + foreach (array_keys($options['headers']) as $k) { + $lkeys[strtolower($k)] = true; + } + // Merge in lowercase default keys when not present in above set. + foreach ($defaults['headers'] as $key => $value) { + if (!isset($lkeys[strtolower($key)])) { + $options['headers'][$key] = $value; + } + } + // No longer need to merge in headers. + unset($defaults['headers']); + } + + $result = array_replace_recursive($defaults, $options); + foreach ($options as $k => $v) { + if ($v === null) { + unset($result[$k]); + } + } + + return $result; + } + + /** + * @deprecated Use {@see GuzzleHttp\Pool} instead. + * @see GuzzleHttp\Pool + */ + public function sendAll($requests, array $options = []) + { + Pool::send($this, $requests, $options); + } + + /** + * @deprecated Use GuzzleHttp\Utils::getDefaultHandler + */ + public static function getDefaultHandler() + { + return Utils::getDefaultHandler(); + } + + /** + * @deprecated Use GuzzleHttp\Utils::getDefaultUserAgent + */ + public static function getDefaultUserAgent() + { + return Utils::getDefaultUserAgent(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/ClientInterface.php new file mode 100644 index 00000000..6668597d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -0,0 +1,150 @@ +data = $data; + } + + /** + * Create a new collection from an array, validate the keys, and add default + * values where missing + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return self + * @throws \InvalidArgumentException if a parameter is missing + */ + public static function fromConfig( + array $config = [], + array $defaults = [], + array $required = [] + ) { + $data = $config + $defaults; + + if ($missing = array_diff($required, array_keys($data))) { + throw new \InvalidArgumentException( + 'Config is missing the following keys: ' . + implode(', ', $missing)); + } + + return new self($data); + } + + /** + * Removes all key value pairs + */ + public function clear() + { + $this->data = []; + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * + * @return mixed|null Value of the key or NULL + */ + public function get($key) + { + return isset($this->data[$key]) ? $this->data[$key] : null; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + */ + public function set($key, $value) + { + $this->data[$key] = $value; + } + + /** + * Add a value to a key. If a key of the same name has already been added, + * the key value will be converted into an array and the new value will be + * pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } elseif (is_array($this->data[$key])) { + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + */ + public function remove($key) + { + unset($this->data[$key]); + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value + * was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data, true); + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + */ + public function replace(array $data) + { + $this->data = $data; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * @param Collection|array $data Associative array of key value pair data + */ + public function merge($data) + { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + } + + /** + * Overwrite key value pairs in this collection with all of the data from + * an array or collection. + * + * @param array|\Traversable $data Values to override over this config + */ + public function overwriteWith($data) + { + if (is_array($data)) { + $this->data = $data + $this->data; + } elseif ($data instanceof Collection) { + $this->data = $data->toArray() + $this->data; + } else { + foreach ($data as $key => $value) { + $this->data[$key] = $value; + } + } + } + + /** + * Returns a Collection containing all the elements of the collection after + * applying the callback function to each one. + * + * The callable should accept three arguments: + * - (string) $key + * - (string) $value + * - (array) $context + * + * The callable must return a the altered or unaltered value. + * + * @param callable $closure Map function to apply + * @param array $context Context to pass to the callable + * + * @return Collection + */ + public function map(callable $closure, array $context = []) + { + $collection = new static(); + foreach ($this as $key => $value) { + $collection[$key] = $closure($key, $value, $context); + } + + return $collection; + } + + /** + * Iterates over each key value pair in the collection passing them to the + * callable. If the callable returns true, the current value from input is + * returned into the result Collection. + * + * The callable must accept two arguments: + * - (string) $key + * - (string) $value + * + * @param callable $closure Evaluation function + * + * @return Collection + */ + public function filter(callable $closure) + { + $collection = new static(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection[$key] = $value; + } + } + + return $collection; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php new file mode 100644 index 00000000..f8ac7dd3 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -0,0 +1,248 @@ +strictMode = $strictMode; + + foreach ($cookieArray as $cookie) { + if (!($cookie instanceof SetCookie)) { + $cookie = new SetCookie($cookie); + } + $this->setCookie($cookie); + } + } + + /** + * Create a new Cookie jar from an associative array and domain. + * + * @param array $cookies Cookies to create the jar from + * @param string $domain Domain to set the cookies to + * + * @return self + */ + public static function fromArray(array $cookies, $domain) + { + $cookieJar = new self(); + foreach ($cookies as $name => $value) { + $cookieJar->setCookie(new SetCookie([ + 'Domain' => $domain, + 'Name' => $name, + 'Value' => $value, + 'Discard' => true + ])); + } + + return $cookieJar; + } + + /** + * Quote the cookie value if it is not already quoted and it contains + * problematic characters. + * + * @param string $value Value that may or may not need to be quoted + * + * @return string + */ + public static function getCookieValue($value) + { + if (substr($value, 0, 1) !== '"' && + substr($value, -1, 1) !== '"' && + strpbrk($value, ';,') + ) { + $value = '"' . $value . '"'; + } + + return $value; + } + + public function toArray() + { + return array_map(function (SetCookie $cookie) { + return $cookie->toArray(); + }, $this->getIterator()->getArrayCopy()); + } + + public function clear($domain = null, $path = null, $name = null) + { + if (!$domain) { + $this->cookies = []; + return; + } elseif (!$path) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !$cookie->matchesDomain($domain); + } + ); + } elseif (!$name) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !($cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } else { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain, $name) { + return !($cookie->getName() == $name && + $cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } + } + + public function clearSessionCookies() + { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + } + ); + } + + public function setCookie(SetCookie $cookie) + { + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new \RuntimeException('Invalid cookie: ' . $result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, and domain are + // identical. + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is + // not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then + // replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + public function count() + { + return count($this->cookies); + } + + public function getIterator() + { + return new \ArrayIterator(array_values($this->cookies)); + } + + public function extractCookies( + RequestInterface $request, + ResponseInterface $response + ) { + if ($cookieHeader = $response->getHeaderAsArray('Set-Cookie')) { + foreach ($cookieHeader as $cookie) { + $sc = SetCookie::fromString($cookie); + if (!$sc->getDomain()) { + $sc->setDomain($request->getHost()); + } + $this->setCookie($sc); + } + } + } + + public function addCookieHeader(RequestInterface $request) + { + $values = []; + $scheme = $request->getScheme(); + $host = $request->getHost(); + $path = $request->getPath(); + + foreach ($this->cookies as $cookie) { + if ($cookie->matchesPath($path) && + $cookie->matchesDomain($host) && + !$cookie->isExpired() && + (!$cookie->getSecure() || $scheme == 'https') + ) { + $values[] = $cookie->getName() . '=' + . self::getCookieValue($cookie->getValue()); + } + } + + if ($values) { + $request->setHeader('Cookie', implode('; ', $values)); + } + } + + /** + * If a cookie already exists and the server asks to set it again with a + * null value, the cookie must be deleted. + * + * @param SetCookie $cookie + */ + private function removeCookieIfEmpty(SetCookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->clear( + $cookie->getDomain(), + $cookie->getPath(), + $cookie->getName() + ); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php new file mode 100644 index 00000000..4ea8567e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -0,0 +1,75 @@ +filename = $cookieFile; + + if (file_exists($cookieFile)) { + $this->load($cookieFile); + } + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->save($this->filename); + } + + /** + * Saves the cookies to a file. + * + * @param string $filename File to save + * @throws \RuntimeException if the file cannot be found or created + */ + public function save($filename) + { + $json = []; + foreach ($this as $cookie) { + if ($cookie->getExpires() && !$cookie->getDiscard()) { + $json[] = $cookie->toArray(); + } + } + + if (false === file_put_contents($filename, json_encode($json))) { + // @codeCoverageIgnoreStart + throw new \RuntimeException("Unable to save file {$filename}"); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load cookies from a JSON formatted file. + * + * Old cookies are kept unless overwritten by newly loaded ones. + * + * @param string $filename Cookie file to load. + * @throws \RuntimeException if the file cannot be loaded. + */ + public function load($filename) + { + $json = file_get_contents($filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new \RuntimeException("Unable to load file {$filename}"); + // @codeCoverageIgnoreEnd + } + + $data = Utils::jsonDecode($json, true); + if (is_array($data)) { + foreach (Utils::jsonDecode($json, true) as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie file: {$filename}"); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php new file mode 100644 index 00000000..71a02d56 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -0,0 +1,66 @@ +sessionKey = $sessionKey; + $this->load(); + } + + /** + * Saves cookies to session when shutting down + */ + public function __destruct() + { + $this->save(); + } + + /** + * Save cookies to the client session + */ + public function save() + { + $json = []; + foreach ($this as $cookie) { + if ($cookie->getExpires() && !$cookie->getDiscard()) { + $json[] = $cookie->toArray(); + } + } + + $_SESSION[$this->sessionKey] = json_encode($json); + } + + /** + * Load the contents of the client session into the data array + */ + protected function load() + { + $cookieJar = isset($_SESSION[$this->sessionKey]) + ? $_SESSION[$this->sessionKey] + : null; + + $data = Utils::jsonDecode($cookieJar, true); + if (is_array($data)) { + foreach ($data as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie data"); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php new file mode 100644 index 00000000..ac9a8908 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -0,0 +1,373 @@ + null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false + ]; + + /** @var array Cookie data */ + private $data; + + /** + * Create a new SetCookie object from a string + * + * @param string $cookie Set-Cookie header string + * + * @return self + */ + public static function fromString($cookie) + { + // Create the default return array + $data = self::$defaults; + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return new self($data); + } + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + $value = isset($cookieParts[1]) + ? trim($cookieParts[1], " \n\r\t\0\x0B\"") + : true; + + // Only check for non-cookies when cookies have been found + if (empty($data['Name'])) { + $data['Name'] = $key; + $data['Value'] = $value; + } else { + foreach (array_keys(self::$defaults) as $search) { + if (!strcasecmp($search, $key)) { + $data[$search] = $value; + continue 2; + } + } + $data[$key] = $value; + } + } + + return new self($data); + } + + /** + * @param array $data Array of cookie data provided by a Cookie parser + */ + public function __construct(array $data = []) + { + $this->data = array_replace(self::$defaults, $data); + // Extract the Expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the Expires date + $this->setExpires(time() + $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires($this->getExpires()); + } + } + + public function __toString() + { + $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; + foreach ($this->data as $k => $v) { + if ($k != 'Name' && $k != 'Value' && $v !== null && $v !== false) { + if ($k == 'Expires') { + $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; + } else { + $str .= ($v === true ? $k : "{$k}={$v}") . '; '; + } + } + } + + return rtrim($str, '; '); + } + + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['Name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + */ + public function setName($name) + { + $this->data['Name'] = $name; + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['Value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + */ + public function setValue($value) + { + $this->data['Value'] = $value; + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['Domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + */ + public function setDomain($domain) + { + $this->data['Domain'] = $domain; + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['Path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + */ + public function setPath($path) + { + $this->data['Path'] = $path; + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['Max-Age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + */ + public function setMaxAge($maxAge) + { + $this->data['Max-Age'] = $maxAge; + } + + /** + * The UNIX timestamp when the cookie Expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['Expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + */ + public function setExpires($timestamp) + { + $this->data['Expires'] = is_numeric($timestamp) + ? (int) $timestamp + : strtotime($timestamp); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['Secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + */ + public function setSecure($secure) + { + $this->data['Secure'] = $secure; + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['Discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + */ + public function setDiscard($discard) + { + $this->data['Discard'] = $discard; + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['HttpOnly']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + */ + public function setHttpOnly($httpOnly) + { + $this->data['HttpOnly'] = $httpOnly; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + return !$this->getPath() || 0 === stripos($path, $this->getPath()); + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + return "Cookie name must not cannot invalid characters: =,; \\t\\r\\n\\013\\014"; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name + // in a private network. + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php new file mode 100644 index 00000000..0d2f4dbf --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php @@ -0,0 +1,20 @@ +propagationStopped; + } + + public function stopPropagation() + { + $this->propagationStopped = true; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php new file mode 100644 index 00000000..8a6ee47c --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php @@ -0,0 +1,61 @@ +transaction = $transaction; + } + + /** + * Get the HTTP client associated with the event. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->transaction->client; + } + + /** + * Get the request object + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->transaction->request; + } + + /** + * Get the number of transaction retries. + * + * @return int + */ + public function getRetryCount() + { + return $this->transaction->retries; + } + + /** + * @return Transaction + */ + public function getTransaction() + { + return $this->transaction; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php new file mode 100644 index 00000000..bbbdfaf8 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php @@ -0,0 +1,40 @@ +transaction->state = 'retry'; + + if ($afterDelay) { + $this->transaction->request->getConfig()->set('delay', $afterDelay); + } + + $this->stopPropagation(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php new file mode 100644 index 00000000..3b106df0 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php @@ -0,0 +1,63 @@ +transaction->transferInfo; + } + + return isset($this->transaction->transferInfo[$name]) + ? $this->transaction->transferInfo[$name] + : null; + } + + /** + * Returns true/false if a response is available. + * + * @return bool + */ + public function hasResponse() + { + return !($this->transaction->response instanceof FutureInterface); + } + + /** + * Get the response. + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->hasResponse() ? $this->transaction->response : null; + } + + /** + * Intercept the request and associate a response + * + * @param ResponseInterface $response Response to set + */ + public function intercept(ResponseInterface $response) + { + $this->transaction->response = $response; + $this->transaction->exception = null; + $this->stopPropagation(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php new file mode 100644 index 00000000..f313c375 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php @@ -0,0 +1,26 @@ +transaction->response = $response; + $this->transaction->exception = null; + $this->stopPropagation(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php new file mode 100644 index 00000000..56cc557e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher + */ +class Emitter implements EmitterInterface +{ + /** @var array */ + private $listeners = []; + + /** @var array */ + private $sorted = []; + + public function on($eventName, callable $listener, $priority = 0) + { + if ($priority === 'first') { + $priority = isset($this->listeners[$eventName]) + ? max(array_keys($this->listeners[$eventName])) + 1 + : 1; + } elseif ($priority === 'last') { + $priority = isset($this->listeners[$eventName]) + ? min(array_keys($this->listeners[$eventName])) - 1 + : -1; + } + + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + public function once($eventName, callable $listener, $priority = 0) + { + $onceListener = function ( + EventInterface $event + ) use (&$onceListener, $eventName, $listener, $priority) { + $this->removeListener($eventName, $onceListener); + $listener($event, $eventName); + }; + + $this->on($eventName, $onceListener, $priority); + } + + public function removeListener($eventName, callable $listener) + { + if (empty($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset( + $this->listeners[$eventName][$priority][$key], + $this->sorted[$eventName] + ); + } + } + } + + public function listeners($eventName = null) + { + // Return all events in a sorted priority order + if ($eventName === null) { + foreach (array_keys($this->listeners) as $eventName) { + if (empty($this->sorted[$eventName])) { + $this->listeners($eventName); + } + } + return $this->sorted; + } + + // Return the listeners for a specific event, sorted in priority order + if (empty($this->sorted[$eventName])) { + $this->sorted[$eventName] = []; + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName], SORT_NUMERIC); + foreach ($this->listeners[$eventName] as $listeners) { + foreach ($listeners as $listener) { + $this->sorted[$eventName][] = $listener; + } + } + } + } + + return $this->sorted[$eventName]; + } + + public function hasListeners($eventName) + { + return !empty($this->listeners[$eventName]); + } + + public function emit($eventName, EventInterface $event) + { + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners($eventName) as $listener) { + $listener($event, $eventName); + if ($event->isPropagationStopped()) { + break; + } + } + } + + return $event; + } + + public function attach(SubscriberInterface $subscriber) + { + foreach ($subscriber->getEvents() as $eventName => $listeners) { + if (is_array($listeners[0])) { + foreach ($listeners as $listener) { + $this->on( + $eventName, + [$subscriber, $listener[0]], + isset($listener[1]) ? $listener[1] : 0 + ); + } + } else { + $this->on( + $eventName, + [$subscriber, $listeners[0]], + isset($listeners[1]) ? $listeners[1] : 0 + ); + } + } + } + + public function detach(SubscriberInterface $subscriber) + { + foreach ($subscriber->getEvents() as $eventName => $listener) { + $this->removeListener($eventName, [$subscriber, $listener[0]]); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php new file mode 100644 index 00000000..9783efd1 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php @@ -0,0 +1,96 @@ +transaction->exception; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php new file mode 100644 index 00000000..7432134d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php @@ -0,0 +1,27 @@ +transaction->exception; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php new file mode 100644 index 00000000..97247e84 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php @@ -0,0 +1,23 @@ +emitter) { + $this->emitter = new Emitter(); + } + + return $this->emitter; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php new file mode 100644 index 00000000..407dc92d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php @@ -0,0 +1,88 @@ +getEmitter(); + foreach ($listeners as $el) { + if ($el['once']) { + $emitter->once($el['name'], $el['fn'], $el['priority']); + } else { + $emitter->on($el['name'], $el['fn'], $el['priority']); + } + } + } + + /** + * Extracts the allowed events from the provided array, and ignores anything + * else in the array. The event listener must be specified as a callable or + * as an array of event listener data ("name", "fn", "priority", "once"). + * + * @param array $source Array containing callables or hashes of data to be + * prepared as event listeners. + * @param array $events Names of events to look for in the provided $source + * array. Other keys are ignored. + * @return array + */ + private function prepareListeners(array $source, array $events) + { + $listeners = []; + foreach ($events as $name) { + if (isset($source[$name])) { + $this->buildListener($name, $source[$name], $listeners); + } + } + + return $listeners; + } + + /** + * Creates a complete event listener definition from the provided array of + * listener data. Also works recursively if more than one listeners are + * contained in the provided array. + * + * @param string $name Name of the event the listener is for. + * @param array|callable $data Event listener data to prepare. + * @param array $listeners Array of listeners, passed by reference. + * + * @throws \InvalidArgumentException if the event data is malformed. + */ + private function buildListener($name, $data, &$listeners) + { + static $defaults = ['priority' => 0, 'once' => false]; + + // If a callable is provided, normalize it to the array format. + if (is_callable($data)) { + $data = ['fn' => $data]; + } + + // Prepare the listener and add it to the array, recursively. + if (isset($data['fn'])) { + $data['name'] = $name; + $listeners[] = $data + $defaults; + } elseif (is_array($data)) { + foreach ($data as $listenerData) { + $this->buildListener($name, $listenerData, $listeners); + } + } else { + throw new \InvalidArgumentException('Each event listener must be a ' + . 'callable or an associative array containing a "fn" key.'); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php new file mode 100644 index 00000000..3fd0de4a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php @@ -0,0 +1,51 @@ +downloadSize = $downloadSize; + $this->downloaded = $downloaded; + $this->uploadSize = $uploadSize; + $this->uploaded = $uploaded; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php new file mode 100644 index 00000000..f51d4206 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php @@ -0,0 +1,56 @@ + ['methodName']] + * - ['eventName' => ['methodName', $priority]] + * - ['eventName' => [['methodName'], ['otherMethod']] + * - ['eventName' => [['methodName'], ['otherMethod', $priority]] + * - ['eventName' => [['methodName', $priority], ['otherMethod', $priority]] + * + * @return array + */ + public function getEvents(); +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php new file mode 100644 index 00000000..fd78431e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php @@ -0,0 +1,7 @@ +response = $response; + } + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php new file mode 100644 index 00000000..3f052d36 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php @@ -0,0 +1,121 @@ +getStatusCode() + : 0; + parent::__construct($message, $code, $previous); + $this->request = $request; + $this->response = $response; + } + + /** + * Wrap non-RequestExceptions with a RequestException + * + * @param RequestInterface $request + * @param \Exception $e + * + * @return RequestException + */ + public static function wrapException(RequestInterface $request, \Exception $e) + { + if ($e instanceof RequestException) { + return $e; + } elseif ($e instanceof ConnectException) { + return new HttpConnectException($e->getMessage(), $request, null, $e); + } else { + return new RequestException($e->getMessage(), $request, null, $e); + } + } + + /** + * Factory method to create a new exception with a normalized error message + * + * @param RequestInterface $request Request + * @param ResponseInterface $response Response received + * @param \Exception $previous Previous exception + * + * @return self + */ + public static function create( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null + ) { + if (!$response) { + return new self('Error completing request', $request, null, $previous); + } + + $level = floor($response->getStatusCode() / 100); + if ($level == '4') { + $label = 'Client error response'; + $className = __NAMESPACE__ . '\\ClientException'; + } elseif ($level == '5') { + $label = 'Server error response'; + $className = __NAMESPACE__ . '\\ServerException'; + } else { + $label = 'Unsuccessful response'; + $className = __CLASS__; + } + + $message = $label . ' [url] ' . $request->getUrl() + . ' [status code] ' . $response->getStatusCode() + . ' [reason phrase] ' . $response->getReasonPhrase(); + + return new $className($message, $request, $response, $previous); + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Check if a response was received + * + * @return bool + */ + public function hasResponse() + { + return $this->response !== null; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php new file mode 100644 index 00000000..7cdd3408 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php @@ -0,0 +1,7 @@ +error = $error; + } + + /** + * Get the associated error + * + * @return \LibXMLError|null + */ + public function getError() + { + return $this->error; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/HasDataTrait.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/HasDataTrait.php new file mode 100644 index 00000000..020dfc9a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/HasDataTrait.php @@ -0,0 +1,75 @@ +data); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + public function toArray() + { + return $this->data; + } + + public function count() + { + return count($this->data); + } + + /** + * Get a value from the collection using a path syntax to retrieve nested + * data. + * + * @param string $path Path to traverse and retrieve a value from + * + * @return mixed|null + */ + public function getPath($path) + { + return Utils::getPath($this->data, $path); + } + + /** + * Set a value into a nested array key. Keys will be created as needed to + * set the value. + * + * @param string $path Path to set + * @param mixed $value Value to set at the key + * + * @throws \RuntimeException when trying to setPath using a nested path + * that travels through a scalar value + */ + public function setPath($path, $value) + { + Utils::setPath($this->data, $path, $value); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php new file mode 100644 index 00000000..f118e0fe --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php @@ -0,0 +1,253 @@ +getBody(); + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function getBody() + { + return $this->body; + } + + public function setBody(StreamInterface $body = null) + { + if ($body === null) { + // Setting a null body will remove the body of the request + $this->removeHeader('Content-Length'); + $this->removeHeader('Transfer-Encoding'); + } + + $this->body = $body; + } + + public function addHeader($header, $value) + { + if (is_array($value)) { + $current = array_merge($this->getHeaderAsArray($header), $value); + } else { + $current = $this->getHeaderAsArray($header); + $current[] = (string) $value; + } + + $this->setHeader($header, $current); + } + + public function addHeaders(array $headers) + { + foreach ($headers as $name => $header) { + $this->addHeader($name, $header); + } + } + + public function getHeader($header) + { + $name = strtolower($header); + return isset($this->headers[$name]) + ? implode(', ', $this->headers[$name]) + : ''; + } + + public function getHeaderAsArray($header) + { + $name = strtolower($header); + return isset($this->headers[$name]) ? $this->headers[$name] : []; + } + + public function getHeaders() + { + $headers = []; + foreach ($this->headers as $name => $values) { + $headers[$this->headerNames[$name]] = $values; + } + + return $headers; + } + + public function setHeader($header, $value) + { + $header = trim($header); + $name = strtolower($header); + $this->headerNames[$name] = $header; + + if (is_array($value)) { + foreach ($value as &$v) { + $v = trim($v); + } + $this->headers[$name] = $value; + } else { + $this->headers[$name] = [trim($value)]; + } + } + + public function setHeaders(array $headers) + { + $this->headers = $this->headerNames = []; + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + } + + public function hasHeader($header) + { + return isset($this->headers[strtolower($header)]); + } + + public function removeHeader($header) + { + $name = strtolower($header); + unset($this->headers[$name], $this->headerNames[$name]); + } + + /** + * Parse an array of header values containing ";" separated data into an + * array of associative arrays representing the header key value pair + * data of the header. When a parameter does not contain a value, but just + * contains a key, this function will inject a key with a '' string value. + * + * @param MessageInterface $message That contains the header + * @param string $header Header to retrieve from the message + * + * @return array Returns the parsed header values. + */ + public static function parseHeader(MessageInterface $message, $header) + { + static $trimmed = "\"' \n\t\r"; + $params = $matches = []; + + foreach (self::normalizeHeader($message, $header) as $val) { + $part = []; + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param MessageInterface $message That contains the header + * @param string $header Header to retrieve from the message + * + * @return array Returns the normalized header field values. + */ + public static function normalizeHeader(MessageInterface $message, $header) + { + $h = $message->getHeaderAsArray($header); + for ($i = 0, $total = count($h); $i < $total; $i++) { + if (strpos($h[$i], ',') === false) { + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $h[$i]) as $v) { + $h[] = trim($v); + } + unset($h[$i]); + } + + return $h; + } + + /** + * Gets the start-line and headers of a message as a string + * + * @param MessageInterface $message + * + * @return string + */ + public static function getStartLineAndHeaders(MessageInterface $message) + { + return static::getStartLine($message) + . self::getHeadersAsString($message); + } + + /** + * Gets the headers of a message as a string + * + * @param MessageInterface $message + * + * @return string + */ + public static function getHeadersAsString(MessageInterface $message) + { + $result = ''; + foreach ($message->getHeaders() as $name => $values) { + $result .= "\r\n{$name}: " . implode(', ', $values); + } + + return $result; + } + + /** + * Gets the start line of a message + * + * @param MessageInterface $message + * + * @return string + * @throws \InvalidArgumentException + */ + public static function getStartLine(MessageInterface $message) + { + if ($message instanceof RequestInterface) { + return trim($message->getMethod() . ' ' + . $message->getResource()) + . ' HTTP/' . $message->getProtocolVersion(); + } elseif ($message instanceof ResponseInterface) { + return 'HTTP/' . $message->getProtocolVersion() . ' ' + . $message->getStatusCode() . ' ' + . $message->getReasonPhrase(); + } else { + throw new \InvalidArgumentException('Unknown message type'); + } + } + + /** + * Accepts and modifies the options provided to the message in the + * constructor. + * + * Can be overridden in subclasses as necessary. + * + * @param array $options Options array passed by reference. + */ + protected function handleOptions(array &$options) + { + if (isset($options['protocol_version'])) { + $this->protocolVersion = $options['protocol_version']; + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php new file mode 100644 index 00000000..ca42f20f --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php @@ -0,0 +1,24 @@ +then($onFulfilled, $onRejected, $onProgress), + [$future, 'wait'], + [$future, 'cancel'] + ); + } + + public function getStatusCode() + { + return $this->_value->getStatusCode(); + } + + public function setStatusCode($code) + { + $this->_value->setStatusCode($code); + } + + public function getReasonPhrase() + { + return $this->_value->getReasonPhrase(); + } + + public function setReasonPhrase($phrase) + { + $this->_value->setReasonPhrase($phrase); + } + + public function getEffectiveUrl() + { + return $this->_value->getEffectiveUrl(); + } + + public function setEffectiveUrl($url) + { + $this->_value->setEffectiveUrl($url); + } + + public function json(array $config = []) + { + return $this->_value->json($config); + } + + public function xml(array $config = []) + { + return $this->_value->xml($config); + } + + public function __toString() + { + try { + return $this->_value->__toString(); + } catch (\Exception $e) { + trigger_error($e->getMessage(), E_USER_WARNING); + return ''; + } + } + + public function getProtocolVersion() + { + return $this->_value->getProtocolVersion(); + } + + public function setBody(StreamInterface $body = null) + { + $this->_value->setBody($body); + } + + public function getBody() + { + return $this->_value->getBody(); + } + + public function getHeaders() + { + return $this->_value->getHeaders(); + } + + public function getHeader($header) + { + return $this->_value->getHeader($header); + } + + public function getHeaderAsArray($header) + { + return $this->_value->getHeaderAsArray($header); + } + + public function hasHeader($header) + { + return $this->_value->hasHeader($header); + } + + public function removeHeader($header) + { + $this->_value->removeHeader($header); + } + + public function addHeader($header, $value) + { + $this->_value->addHeader($header, $value); + } + + public function addHeaders(array $headers) + { + $this->_value->addHeaders($headers); + } + + public function setHeader($header, $value) + { + $this->_value->setHeader($header, $value); + } + + public function setHeaders(array $headers) + { + $this->_value->setHeaders($headers); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php new file mode 100644 index 00000000..d469ef1e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php @@ -0,0 +1,364 @@ + 1, 'timeout' => 1, 'verify' => 1, 'ssl_key' => 1, + 'cert' => 1, 'proxy' => 1, 'debug' => 1, 'save_to' => 1, 'stream' => 1, + 'expect' => 1, 'future' => 1 + ]; + + /** @var array Default allow_redirects request option settings */ + private static $defaultRedirect = [ + 'max' => 5, + 'strict' => false, + 'referer' => false, + 'protocols' => ['http', 'https'] + ]; + + /** + * @param array $customOptions Associative array of custom request option + * names mapping to functions used to apply + * the option. The function accepts the request + * and the option value to apply. + */ + public function __construct(array $customOptions = []) + { + $this->errorPlugin = new HttpError(); + $this->redirectPlugin = new Redirect(); + $this->customOptions = $customOptions; + } + + public function createResponse( + $statusCode, + array $headers = [], + $body = null, + array $options = [] + ) { + if (null !== $body) { + $body = Stream::factory($body); + } + + return new Response($statusCode, $headers, $body, $options); + } + + public function createRequest($method, $url, array $options = []) + { + // Handle the request protocol version option that needs to be + // specified in the request constructor. + if (isset($options['version'])) { + $options['config']['protocol_version'] = $options['version']; + unset($options['version']); + } + + $request = new Request($method, $url, [], null, + isset($options['config']) ? $options['config'] : []); + + unset($options['config']); + + // Use a POST body by default + if (strtoupper($method) == 'POST' + && !isset($options['body']) + && !isset($options['json']) + ) { + $options['body'] = []; + } + + if ($options) { + $this->applyOptions($request, $options); + } + + return $request; + } + + /** + * Create a request or response object from an HTTP message string + * + * @param string $message Message to parse + * + * @return RequestInterface|ResponseInterface + * @throws \InvalidArgumentException if unable to parse a message + */ + public function fromMessage($message) + { + static $parser; + if (!$parser) { + $parser = new MessageParser(); + } + + // Parse a response + if (strtoupper(substr($message, 0, 4)) == 'HTTP') { + $data = $parser->parseResponse($message); + return $this->createResponse( + $data['code'], + $data['headers'], + $data['body'] === '' ? null : $data['body'], + $data + ); + } + + // Parse a request + if (!($data = ($parser->parseRequest($message)))) { + throw new \InvalidArgumentException('Unable to parse request'); + } + + return $this->createRequest( + $data['method'], + Url::buildUrl($data['request_url']), + [ + 'headers' => $data['headers'], + 'body' => $data['body'] === '' ? null : $data['body'], + 'config' => [ + 'protocol_version' => $data['protocol_version'] + ] + ] + ); + } + + /** + * Apply POST fields and files to a request to attempt to give an accurate + * representation. + * + * @param RequestInterface $request Request to update + * @param array $body Body to apply + */ + protected function addPostData(RequestInterface $request, array $body) + { + static $fields = ['string' => true, 'array' => true, 'NULL' => true, + 'boolean' => true, 'double' => true, 'integer' => true]; + + $post = new PostBody(); + foreach ($body as $key => $value) { + if (isset($fields[gettype($value)])) { + $post->setField($key, $value); + } elseif ($value instanceof PostFileInterface) { + $post->addFile($value); + } else { + $post->addFile(new PostFile($key, $value)); + } + } + + if ($request->getHeader('Content-Type') == 'multipart/form-data') { + $post->forceMultipartUpload(true); + } + + $request->setBody($post); + } + + protected function applyOptions( + RequestInterface $request, + array $options = [] + ) { + $config = $request->getConfig(); + $emitter = $request->getEmitter(); + + foreach ($options as $key => $value) { + + if (isset(self::$configMap[$key])) { + $config[$key] = $value; + continue; + } + + switch ($key) { + + case 'allow_redirects': + + if ($value === false) { + continue; + } + + if ($value === true) { + $value = self::$defaultRedirect; + } elseif (!is_array($value)) { + throw new Iae('allow_redirects must be true, false, or array'); + } else { + // Merge the default settings with the provided settings + $value += self::$defaultRedirect; + } + + $config['redirect'] = $value; + $emitter->attach($this->redirectPlugin); + break; + + case 'decode_content': + + if ($value === false) { + continue; + } + + $config['decode_content'] = true; + if ($value !== true) { + $request->setHeader('Accept-Encoding', $value); + } + break; + + case 'headers': + + if (!is_array($value)) { + throw new Iae('header value must be an array'); + } + foreach ($value as $k => $v) { + $request->setHeader($k, $v); + } + break; + + case 'exceptions': + + if ($value === true) { + $emitter->attach($this->errorPlugin); + } + break; + + case 'body': + + if (is_array($value)) { + $this->addPostData($request, $value); + } elseif ($value !== null) { + $request->setBody(Stream::factory($value)); + } + break; + + case 'auth': + + if (!$value) { + continue; + } + + if (is_array($value)) { + $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; + } else { + $type = strtolower($value); + } + + $config['auth'] = $value; + + if ($type == 'basic') { + $request->setHeader( + 'Authorization', + 'Basic ' . base64_encode("$value[0]:$value[1]") + ); + } elseif ($type == 'digest') { + // @todo: Do not rely on curl + $config->setPath('curl/' . CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + $config->setPath('curl/' . CURLOPT_USERPWD, "$value[0]:$value[1]"); + } + break; + + case 'query': + + if ($value instanceof Query) { + $original = $request->getQuery(); + // Do not overwrite existing query string variables by + // overwriting the object with the query string data passed + // in the URL + $value->overwriteWith($original->toArray()); + $request->setQuery($value); + } elseif (is_array($value)) { + // Do not overwrite existing query string variables + $query = $request->getQuery(); + foreach ($value as $k => $v) { + if (!isset($query[$k])) { + $query[$k] = $v; + } + } + } else { + throw new Iae('query must be an array or Query object'); + } + break; + + case 'cookies': + + if ($value === true) { + static $cookie = null; + if (!$cookie) { + $cookie = new Cookie(); + } + $emitter->attach($cookie); + } elseif (is_array($value)) { + $emitter->attach( + new Cookie(CookieJar::fromArray($value, $request->getHost())) + ); + } elseif ($value instanceof CookieJarInterface) { + $emitter->attach(new Cookie($value)); + } elseif ($value !== false) { + throw new Iae('cookies must be an array, true, or CookieJarInterface'); + } + break; + + case 'events': + + if (!is_array($value)) { + throw new Iae('events must be an array'); + } + + $this->attachListeners($request, + $this->prepareListeners( + $value, + ['before', 'complete', 'error', 'progress', 'end'] + ) + ); + break; + + case 'subscribers': + + if (!is_array($value)) { + throw new Iae('subscribers must be an array'); + } + + foreach ($value as $subscribers) { + $emitter->attach($subscribers); + } + break; + + case 'json': + + $request->setBody(Stream::factory(json_encode($value))); + if (!$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', 'application/json'); + } + break; + + default: + + // Check for custom handler functions. + if (isset($this->customOptions[$key])) { + $fn = $this->customOptions[$key]; + $fn($request, $value); + continue; + } + + throw new Iae("No method can handle the {$key} config key"); + } + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php new file mode 100644 index 00000000..86ae9c7e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php @@ -0,0 +1,71 @@ +getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * @return array Returns an associative array of the message's headers. + */ + public function getHeaders(); + + /** + * Retrieve a header by the given case-insensitive name. + * + * @param string $header Case-insensitive header name. + * + * @return string + */ + public function getHeader($header); + + /** + * Retrieves a header by the given case-insensitive name as an array of strings. + * + * @param string $header Case-insensitive header name. + * + * @return string[] + */ + public function getHeaderAsArray($header); + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $header Case-insensitive header name. + * + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader($header); + + /** + * Remove a specific header by case-insensitive name. + * + * @param string $header Case-insensitive header name. + */ + public function removeHeader($header); + + /** + * Appends a header value to any existing values associated with the + * given header name. + * + * @param string $header Header name to add + * @param string $value Value of the header + */ + public function addHeader($header, $value); + + /** + * Merges in an associative array of headers. + * + * Each array key MUST be a string representing the case-insensitive name + * of a header. Each value MUST be either a string or an array of strings. + * For each value, the value is appended to any existing header of the same + * name, or, if a header does not already exist by the given name, then the + * header is added. + * + * @param array $headers Associative array of headers to add to the message + */ + public function addHeaders(array $headers); + + /** + * Sets a header, replacing any existing values of any headers with the + * same case-insensitive name. + * + * The header values MUST be a string or an array of strings. + * + * @param string $header Header name + * @param string|array $value Header value(s) + */ + public function setHeader($header, $value); + + /** + * Sets headers, replacing any headers that have already been set on the + * message. + * + * The array keys MUST be a string. The array values must be either a + * string or an array of strings. + * + * @param array $headers Headers to set. + */ + public function setHeaders(array $headers); +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php new file mode 100644 index 00000000..c3cc195e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php @@ -0,0 +1,171 @@ +parseMessage($message))) { + return false; + } + + // Parse the protocol and protocol version + if (isset($parts['start_line'][2])) { + $startParts = explode('/', $parts['start_line'][2]); + $protocol = strtoupper($startParts[0]); + $version = isset($startParts[1]) ? $startParts[1] : '1.1'; + } else { + $protocol = 'HTTP'; + $version = '1.1'; + } + + $parsed = [ + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => $protocol, + 'protocol_version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ]; + + $parsed['request_url'] = $this->getUrlPartsFromMessage( + (isset($parts['start_line'][1]) ? $parts['start_line'][1] : ''), $parsed); + + return $parsed; + } + + /** + * Parse an HTTP response message into an associative array of parts. + * + * @param string $message HTTP response to parse + * + * @return array|bool Returns false if the message is invalid + */ + public function parseResponse($message) + { + if (!($parts = $this->parseMessage($message))) { + return false; + } + + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return [ + 'protocol' => $protocol, + 'protocol_version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ]; + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array|bool + */ + private function parseMessage($message) + { + if (!$message) { + return false; + } + + $startLine = null; + $headers = []; + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + $startLine = explode(' ', $line, 3); + } elseif (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = [$headers[$key], $value]; + } else { + $headers[$key][] = $value; + } + } + } + + return [ + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ]; + } + + /** + * Create URL parts from HTTP message parts + * + * @param string $requestUrl Associated URL + * @param array $parts HTTP message parts + * + * @return array + */ + private function getUrlPartsFromMessage($requestUrl, array $parts) + { + // Parse the URL information from the message + $urlParts = ['path' => $requestUrl, 'scheme' => 'http']; + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = null; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/Request.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/Request.php new file mode 100644 index 00000000..38714af8 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/Request.php @@ -0,0 +1,195 @@ +setUrl($url); + $this->method = strtoupper($method); + $this->handleOptions($options); + $this->transferOptions = new Collection($options); + $this->addPrepareEvent(); + + if ($body !== null) { + $this->setBody($body); + } + + if ($headers) { + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + } + } + + public function __clone() + { + if ($this->emitter) { + $this->emitter = clone $this->emitter; + } + $this->transferOptions = clone $this->transferOptions; + $this->url = clone $this->url; + } + + public function setUrl($url) + { + $this->url = $url instanceof Url ? $url : Url::fromString($url); + $this->updateHostHeaderFromUrl(); + } + + public function getUrl() + { + return (string) $this->url; + } + + public function setQuery($query) + { + $this->url->setQuery($query); + } + + public function getQuery() + { + return $this->url->getQuery(); + } + + public function setMethod($method) + { + $this->method = strtoupper($method); + } + + public function getMethod() + { + return $this->method; + } + + public function getScheme() + { + return $this->url->getScheme(); + } + + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + } + + public function getPort() + { + return $this->url->getPort(); + } + + public function setPort($port) + { + $this->url->setPort($port); + $this->updateHostHeaderFromUrl(); + } + + public function getHost() + { + return $this->url->getHost(); + } + + public function setHost($host) + { + $this->url->setHost($host); + $this->updateHostHeaderFromUrl(); + } + + public function getPath() + { + return '/' . ltrim($this->url->getPath(), '/'); + } + + public function setPath($path) + { + $this->url->setPath($path); + } + + public function getResource() + { + $resource = $this->getPath(); + if ($query = (string) $this->url->getQuery()) { + $resource .= '?' . $query; + } + + return $resource; + } + + public function getConfig() + { + return $this->transferOptions; + } + + protected function handleOptions(array &$options) + { + parent::handleOptions($options); + // Use a custom emitter if one is specified, and remove it from + // options that are exposed through getConfig() + if (isset($options['emitter'])) { + $this->emitter = $options['emitter']; + unset($options['emitter']); + } + } + + /** + * Adds a subscriber that ensures a request's body is prepared before + * sending. + */ + private function addPrepareEvent() + { + static $subscriber; + if (!$subscriber) { + $subscriber = new Prepare(); + } + + $this->getEmitter()->attach($subscriber); + } + + private function updateHostHeaderFromUrl() + { + $port = $this->url->getPort(); + $scheme = $this->url->getScheme(); + if ($host = $this->url->getHost()) { + if (($port == 80 && $scheme == 'http') || + ($port == 443 && $scheme == 'https') + ) { + $this->setHeader('Host', $host); + } else { + $this->setHeader('Host', "{$host}:{$port}"); + } + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php new file mode 100644 index 00000000..f6a69d1e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php @@ -0,0 +1,136 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Reserved for WebDAV advanced collections expired proposal', + 426 => 'Upgrade required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ]; + + /** @var string The reason phrase of the response (human readable code) */ + private $reasonPhrase; + + /** @var string The status code of the response */ + private $statusCode; + + /** @var string The effective URL that returned this response */ + private $effectiveUrl; + + /** + * @param int|string $statusCode The response status code (e.g. 200) + * @param array $headers The response headers + * @param StreamInterface $body The body of the response + * @param array $options Response message options + * - reason_phrase: Set a custom reason phrase + * - protocol_version: Set a custom protocol version + */ + public function __construct( + $statusCode, + array $headers = [], + StreamInterface $body = null, + array $options = [] + ) { + $this->statusCode = (int) $statusCode; + $this->handleOptions($options); + + // Assume a reason phrase if one was not applied as an option + if (!$this->reasonPhrase && + isset(self::$statusTexts[$this->statusCode]) + ) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } + + if ($headers) { + $this->setHeaders($headers); + } + + if ($body) { + $this->setBody($body); + } + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function setStatusCode($code) + { + return $this->statusCode = (int) $code; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + public function setReasonPhrase($phrase) + { + return $this->reasonPhrase = $phrase; + } + + public function json(array $config = []) + { + try { + return Utils::jsonDecode( + (string) $this->getBody(), + isset($config['object']) ? !$config['object'] : true, + 512, + isset($config['big_int_strings']) ? JSON_BIGINT_AS_STRING : 0 + ); + } catch (\InvalidArgumentException $e) { + throw new ParseException( + $e->getMessage(), + $this + ); + } + } + + public function xml(array $config = []) + { + $disableEntities = libxml_disable_entity_loader(true); + $internalErrors = libxml_use_internal_errors(true); + + try { + // Allow XML to be retrieved even if there is no response body + $xml = new \SimpleXMLElement( + (string) $this->getBody() ?: '', + isset($config['libxml_options']) ? $config['libxml_options'] : LIBXML_NONET, + false, + isset($config['ns']) ? $config['ns'] : '', + isset($config['ns_is_prefix']) ? $config['ns_is_prefix'] : false + ); + libxml_disable_entity_loader($disableEntities); + libxml_use_internal_errors($internalErrors); + } catch (\Exception $e) { + libxml_disable_entity_loader($disableEntities); + libxml_use_internal_errors($internalErrors); + throw new XmlParseException( + 'Unable to parse response body into XML: ' . $e->getMessage(), + $this, + $e, + (libxml_get_last_error()) ?: null + ); + } + + return $xml; + } + + public function getEffectiveUrl() + { + return $this->effectiveUrl; + } + + public function setEffectiveUrl($url) + { + $this->effectiveUrl = $url; + } + + /** + * Accepts and modifies the options provided to the response in the + * constructor. + * + * @param array $options Options array passed by reference. + */ + protected function handleOptions(array &$options = []) + { + parent::handleOptions($options); + if (isset($options['reason_phrase'])) { + $this->reasonPhrase = $options['reason_phrase']; + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php new file mode 100644 index 00000000..c0ae9be9 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php @@ -0,0 +1,111 @@ + 'text/vnd.in3d.3dml', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'application' => 'application/x-ms-application', + 'apr' => 'application/vnd.lotus-approach', + 'asa' => 'text/plain', + 'asax' => 'application/octet-stream', + 'asc' => 'application/pgp-signature', + 'ascx' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'ashx' => 'text/plain', + 'asm' => 'text/x-asm', + 'asmx' => 'text/plain', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asp' => 'text/plain', + 'aspx' => 'text/plain', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'axd' => 'text/plain', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfc' => 'application/x-coldfusion', + 'cfm' => 'application/x-coldfusion', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'cryptonote' => 'application/vnd.rig.cryptonote', + 'cs' => 'text/plain', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxt' => 'application/vnd.geonext', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hta' => 'application/octet-stream', + 'htc' => 'text/html', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/octet-stream', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/octet-stream', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/octet-stream', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/mp4', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsf' => 'application/vnd.lotus-notes', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'text/x-php', + 'phps' => 'application/x-httpd-phps', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rb' => 'text/plain', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'resx' => 'text/xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sig' => 'application/pgp-signature', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'src' => 'application/x-wais-source', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'image/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvx' => 'application/vnd.dece.unspecified', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-ms-wmz', + 'woff' => 'application/x-font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'yaml' => 'text/yaml', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'yml' => 'text/yaml', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml' + ); + + /** + * Get a singleton instance of the class + * + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get a mimetype value from a file extension + * + * @param string $extension File extension + * + * @return string|null + * + */ + public function fromExtension($extension) + { + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) + ? $this->mimetypes[$extension] + : null; + } + + /** + * Get a mimetype from a filename + * + * @param string $filename Filename to generate a mimetype from + * + * @return string|null + */ + public function fromFilename($filename) + { + return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Pool.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Pool.php new file mode 100644 index 00000000..7b9d83a4 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Pool.php @@ -0,0 +1,333 @@ +client = $client; + $this->iter = $this->coerceIterable($requests); + $this->deferred = new Deferred(); + $this->promise = $this->deferred->promise(); + $this->poolSize = isset($options['pool_size']) + ? $options['pool_size'] : 25; + $this->eventListeners = $this->prepareListeners( + $options, + ['before', 'complete', 'error', 'end'] + ); + } + + /** + * Sends multiple requests in parallel and returns an array of responses + * and exceptions that uses the same ordering as the provided requests. + * + * IMPORTANT: This method keeps every request and response in memory, and + * as such, is NOT recommended when sending a large number or an + * indeterminate number of requests concurrently. + * + * @param ClientInterface $client Client used to send the requests + * @param array|\Iterator $requests Requests to send in parallel + * @param array $options Passes through the options available in + * {@see GuzzleHttp\Pool::__construct} + * + * @return BatchResults Returns a container for the results. + * @throws \InvalidArgumentException if the event format is incorrect. + */ + public static function batch( + ClientInterface $client, + $requests, + array $options = [] + ) { + $hash = new \SplObjectStorage(); + foreach ($requests as $request) { + $hash->attach($request); + } + + // In addition to the normally run events when requests complete, add + // and event to continuously track the results of transfers in the hash. + (new self($client, $requests, RequestEvents::convertEventArray( + $options, + ['end'], + [ + 'priority' => RequestEvents::LATE, + 'fn' => function (EndEvent $e) use ($hash) { + $hash[$e->getRequest()] = $e->getException() + ? $e->getException() + : $e->getResponse(); + } + ] + )))->wait(); + + return new BatchResults($hash); + } + + /** + * Creates a Pool and immediately sends the requests. + * + * @param ClientInterface $client Client used to send the requests + * @param array|\Iterator $requests Requests to send in parallel + * @param array $options Passes through the options available in + * {@see GuzzleHttp\Pool::__construct} + */ + public static function send( + ClientInterface $client, + $requests, + array $options = [] + ) { + $pool = new self($client, $requests, $options); + $pool->wait(); + } + + private function getPoolSize() + { + return is_callable($this->poolSize) + ? call_user_func($this->poolSize, count($this->waitQueue)) + : $this->poolSize; + } + + /** + * Add as many requests as possible up to the current pool limit. + */ + private function addNextRequests() + { + $limit = max($this->getPoolSize() - count($this->waitQueue), 0); + while ($limit--) { + if (!$this->addNextRequest()) { + break; + } + } + } + + public function wait() + { + if ($this->isRealized) { + return false; + } + + // Seed the pool with N number of requests. + $this->addNextRequests(); + + // Stop if the pool was cancelled while transferring requests. + if ($this->isRealized) { + return false; + } + + // Wait on any outstanding FutureResponse objects. + while ($response = array_pop($this->waitQueue)) { + try { + $response->wait(); + } catch (\Exception $e) { + // Eat exceptions because they should be handled asynchronously + } + $this->addNextRequests(); + } + + // Clean up no longer needed state. + $this->isRealized = true; + $this->waitQueue = $this->eventListeners = []; + $this->client = $this->iter = null; + $this->deferred->resolve(true); + + return true; + } + + /** + * {@inheritdoc} + * + * Attempt to cancel all outstanding requests (requests that are queued for + * dereferencing). Returns true if all outstanding requests can be + * cancelled. + * + * @return bool + */ + public function cancel() + { + if ($this->isRealized) { + return false; + } + + $success = $this->isRealized = true; + foreach ($this->waitQueue as $response) { + if (!$response->cancel()) { + $success = false; + } + } + + return $success; + } + + /** + * Returns a promise that is invoked when the pool completed. There will be + * no passed value. + * + * {@inheritdoc} + */ + public function then( + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return $this->promise->then($onFulfilled, $onRejected, $onProgress); + } + + public function promise() + { + return $this->promise; + } + + private function coerceIterable($requests) + { + if ($requests instanceof \Iterator) { + return $requests; + } elseif (is_array($requests)) { + return new \ArrayIterator($requests); + } + + throw new \InvalidArgumentException('Expected Iterator or array. ' + . 'Found ' . Core::describeType($requests)); + } + + /** + * Adds the next request to pool and tracks what requests need to be + * dereferenced when completing the pool. + */ + private function addNextRequest() + { + add_next: + + if ($this->isRealized || !$this->iter || !$this->iter->valid()) { + return false; + } + + $request = $this->iter->current(); + $this->iter->next(); + + if (!($request instanceof RequestInterface)) { + throw new \InvalidArgumentException(sprintf( + 'All requests in the provided iterator must implement ' + . 'RequestInterface. Found %s', + Core::describeType($request) + )); + } + + // Be sure to use "lazy" futures, meaning they do not send right away. + $request->getConfig()->set('future', 'lazy'); + $hash = spl_object_hash($request); + $this->attachListeners($request, $this->eventListeners); + $request->getEmitter()->on('before', [$this, '_trackRetries'], RequestEvents::EARLY); + $response = $this->client->send($request); + $this->waitQueue[$hash] = $response; + $promise = $response->promise(); + + // Don't recursively call itself for completed or rejected responses. + if ($promise instanceof FulfilledPromise + || $promise instanceof RejectedPromise + ) { + try { + $this->finishResponse($request, $response->wait(), $hash); + } catch (\Exception $e) { + $this->finishResponse($request, $e, $hash); + } + goto add_next; + } + + // Use this function for both resolution and rejection. + $thenFn = function ($value) use ($request, $hash) { + $this->finishResponse($request, $value, $hash); + if (!$request->getConfig()->get('_pool_retries')) { + $this->addNextRequests(); + } + }; + + $promise->then($thenFn, $thenFn); + + return true; + } + + public function _trackRetries(BeforeEvent $e) + { + $e->getRequest()->getConfig()->set('_pool_retries', $e->getRetryCount()); + } + + private function finishResponse($request, $value, $hash) + { + unset($this->waitQueue[$hash]); + $result = $value instanceof ResponseInterface + ? ['request' => $request, 'response' => $value, 'error' => null] + : ['request' => $request, 'response' => null, 'error' => $value]; + $this->deferred->notify($result); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php new file mode 100644 index 00000000..1149e623 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php @@ -0,0 +1,109 @@ +boundary = $boundary ?: uniqid(); + $this->stream = $this->createStream($fields, $files); + } + + /** + * Get the boundary + * + * @return string + */ + public function getBoundary() + { + return $this->boundary; + } + + public function isWritable() + { + return false; + } + + /** + * Get the string needed to transfer a POST field + */ + private function getFieldString($name, $value) + { + return sprintf( + "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", + $this->boundary, + $name, + $value + ); + } + + /** + * Get the headers needed before transferring the content of a POST file + */ + private function getFileHeaders(PostFileInterface $file) + { + $headers = ''; + foreach ($file->getHeaders() as $key => $value) { + $headers .= "{$key}: {$value}\r\n"; + } + + return "--{$this->boundary}\r\n" . trim($headers) . "\r\n\r\n"; + } + + /** + * Create the aggregate stream that will be used to upload the POST data + */ + protected function createStream(array $fields, array $files) + { + $stream = new AppendStream(); + + foreach ($fields as $name => $fieldValues) { + foreach ((array) $fieldValues as $value) { + $stream->addStream( + Stream::factory($this->getFieldString($name, $value)) + ); + } + } + + foreach ($files as $file) { + + if (!$file instanceof PostFileInterface) { + throw new \InvalidArgumentException('All POST fields must ' + . 'implement PostFieldInterface'); + } + + $stream->addStream( + Stream::factory($this->getFileHeaders($file)) + ); + $stream->addStream($file->getContent()); + $stream->addStream(Stream::factory("\r\n")); + } + + // Add the trailing boundary with CRLF + $stream->addStream(Stream::factory("--{$this->boundary}--\r\n")); + + return $stream; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostBody.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostBody.php new file mode 100644 index 00000000..ed14d1f7 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostBody.php @@ -0,0 +1,287 @@ +files || $this->forceMultipart) { + $request->setHeader( + 'Content-Type', + 'multipart/form-data; boundary=' . $this->getBody()->getBoundary() + ); + } elseif ($this->fields && !$request->hasHeader('Content-Type')) { + $request->setHeader( + 'Content-Type', + 'application/x-www-form-urlencoded' + ); + } + + if ($size = $this->getSize()) { + $request->setHeader('Content-Length', $size); + } + } + + public function forceMultipartUpload($force) + { + $this->forceMultipart = $force; + } + + public function setAggregator(callable $aggregator) + { + $this->aggregator = $aggregator; + } + + public function setField($name, $value) + { + $this->fields[$name] = $value; + $this->mutate(); + } + + public function replaceFields(array $fields) + { + $this->fields = $fields; + $this->mutate(); + } + + public function getField($name) + { + return isset($this->fields[$name]) ? $this->fields[$name] : null; + } + + public function removeField($name) + { + unset($this->fields[$name]); + $this->mutate(); + } + + public function getFields($asString = false) + { + if (!$asString) { + return $this->fields; + } + + $query = new Query($this->fields); + $query->setEncodingType(Query::RFC1738); + $query->setAggregator($this->getAggregator()); + + return (string) $query; + } + + public function hasField($name) + { + return isset($this->fields[$name]); + } + + public function getFile($name) + { + foreach ($this->files as $file) { + if ($file->getName() == $name) { + return $file; + } + } + + return null; + } + + public function getFiles() + { + return $this->files; + } + + public function addFile(PostFileInterface $file) + { + $this->files[] = $file; + $this->mutate(); + } + + public function clearFiles() + { + $this->files = []; + $this->mutate(); + } + + /** + * Returns the numbers of fields + files + * + * @return int + */ + public function count() + { + return count($this->files) + count($this->fields); + } + + public function __toString() + { + return (string) $this->getBody(); + } + + public function getContents($maxLength = -1) + { + return $this->getBody()->getContents(); + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->detached = true; + $this->fields = $this->files = []; + + if ($this->body) { + $this->body->close(); + $this->body = null; + } + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function eof() + { + return $this->getBody()->eof(); + } + + public function tell() + { + return $this->body ? $this->body->tell() : 0; + } + + public function isSeekable() + { + return true; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function getSize() + { + return $this->getBody()->getSize(); + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->getBody()->seek($offset, $whence); + } + + public function read($length) + { + return $this->getBody()->read($length); + } + + public function write($string) + { + return false; + } + + public function getMetadata($key = null) + { + return $key ? null : []; + } + + /** + * Return a stream object that is built from the POST fields and files. + * + * If one has already been created, the previously created stream will be + * returned. + */ + private function getBody() + { + if ($this->body) { + return $this->body; + } elseif ($this->files || $this->forceMultipart) { + return $this->body = $this->createMultipart(); + } elseif ($this->fields) { + return $this->body = $this->createUrlEncoded(); + } else { + return $this->body = Stream::factory(); + } + } + + /** + * Get the aggregator used to join multi-valued field parameters + * + * @return callable + */ + final protected function getAggregator() + { + if (!$this->aggregator) { + $this->aggregator = Query::phpAggregator(); + } + + return $this->aggregator; + } + + /** + * Creates a multipart/form-data body stream + * + * @return MultipartBody + */ + private function createMultipart() + { + // Flatten the nested query string values using the correct aggregator + return new MultipartBody( + call_user_func($this->getAggregator(), $this->fields), + $this->files + ); + } + + /** + * Creates an application/x-www-form-urlencoded stream body + * + * @return StreamInterface + */ + private function createUrlEncoded() + { + return Stream::factory($this->getFields(true)); + } + + /** + * Get rid of any cached data + */ + private function mutate() + { + $this->body = null; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php new file mode 100644 index 00000000..c2ec9a62 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php @@ -0,0 +1,109 @@ +headers = $headers; + $this->name = $name; + $this->prepareContent($content); + $this->prepareFilename($filename); + $this->prepareDefaultHeaders(); + } + + public function getName() + { + return $this->name; + } + + public function getFilename() + { + return $this->filename; + } + + public function getContent() + { + return $this->content; + } + + public function getHeaders() + { + return $this->headers; + } + + /** + * Prepares the contents of a POST file. + * + * @param mixed $content Content of the POST file + */ + private function prepareContent($content) + { + $this->content = $content; + + if (!($this->content instanceof StreamInterface)) { + $this->content = Stream::factory($this->content); + } elseif ($this->content instanceof MultipartBody) { + if (!$this->hasHeader('Content-Disposition')) { + $disposition = 'form-data; name="' . $this->name .'"'; + $this->headers['Content-Disposition'] = $disposition; + } + + if (!$this->hasHeader('Content-Type')) { + $this->headers['Content-Type'] = sprintf( + "multipart/form-data; boundary=%s", + $this->content->getBoundary() + ); + } + } + } + + /** + * Applies a file name to the POST file based on various checks. + * + * @param string|null $filename Filename to apply (or null to guess) + */ + private function prepareFilename($filename) + { + $this->filename = $filename; + + if (!$this->filename) { + $this->filename = $this->content->getMetadata('uri'); + } + + if (!$this->filename || substr($this->filename, 0, 6) === 'php://') { + $this->filename = $this->name; + } + } + + /** + * Applies default Content-Disposition and Content-Type headers if needed. + */ + private function prepareDefaultHeaders() + { + // Set a default content-disposition header if one was no provided + if (!$this->hasHeader('Content-Disposition')) { + $this->headers['Content-Disposition'] = sprintf( + 'form-data; name="%s"; filename="%s"', + $this->name, + basename($this->filename) + ); + } + + // Set a default Content-Type if one was not supplied + if (!$this->hasHeader('Content-Type')) { + $this->headers['Content-Type'] = Mimetypes::getInstance() + ->fromFilename($this->filename) ?: 'text/plain'; + } + } + + /** + * Check if a specific header exists on the POST file by name. + * + * @param string $name Case-insensitive header to check + * + * @return bool + */ + private function hasHeader($name) + { + return isset(array_change_key_case($this->headers)[strtolower($name)]); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php new file mode 100644 index 00000000..2e816c08 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php @@ -0,0 +1,41 @@ +setEncodingType($urlEncoding); + } + + $qp->parseInto($q, $query, $urlEncoding); + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + */ + public function __toString() + { + if (!$this->data) { + return ''; + } + + // The default aggregator is statically cached + static $defaultAggregator; + + if (!$this->aggregator) { + if (!$defaultAggregator) { + $defaultAggregator = self::phpAggregator(); + } + $this->aggregator = $defaultAggregator; + } + + $result = ''; + $aggregator = $this->aggregator; + $encoder = $this->encoding; + + foreach ($aggregator($this->data) as $key => $values) { + foreach ($values as $value) { + if ($result) { + $result .= '&'; + } + $result .= $encoder($key); + if ($value !== null) { + $result .= '=' . $encoder($value); + } + } + } + + return $result; + } + + /** + * Controls how multi-valued query string parameters are aggregated into a + * string. + * + * $query->setAggregator($query::duplicateAggregator()); + * + * @param callable $aggregator Callable used to convert a deeply nested + * array of query string variables into a flattened array of key value + * pairs. The callable accepts an array of query data and returns a + * flattened array of key value pairs where each value is an array of + * strings. + */ + public function setAggregator(callable $aggregator) + { + $this->aggregator = $aggregator; + } + + /** + * Specify how values are URL encoded + * + * @param string|bool $type One of 'RFC1738', 'RFC3986', or false to disable encoding + * + * @throws \InvalidArgumentException + */ + public function setEncodingType($type) + { + switch ($type) { + case self::RFC3986: + $this->encoding = 'rawurlencode'; + break; + case self::RFC1738: + $this->encoding = 'urlencode'; + break; + case false: + $this->encoding = function ($v) { return $v; }; + break; + default: + throw new \InvalidArgumentException('Invalid URL encoding type'); + } + } + + /** + * Query string aggregator that does not aggregate nested query string + * values and allows duplicates in the resulting array. + * + * Example: http://test.com?q=1&q=2 + * + * @return callable + */ + public static function duplicateAggregator() + { + return function (array $data) { + return self::walkQuery($data, '', function ($key, $prefix) { + return is_int($key) ? $prefix : "{$prefix}[{$key}]"; + }); + }; + } + + /** + * Aggregates nested query string variables using the same technique as + * ``http_build_query()``. + * + * @param bool $numericIndices Pass false to not include numeric indices + * when multi-values query string parameters are present. + * + * @return callable + */ + public static function phpAggregator($numericIndices = true) + { + return function (array $data) use ($numericIndices) { + return self::walkQuery( + $data, + '', + function ($key, $prefix) use ($numericIndices) { + return !$numericIndices && is_int($key) + ? "{$prefix}[]" + : "{$prefix}[{$key}]"; + } + ); + }; + } + + /** + * Easily create query aggregation functions by providing a key prefix + * function to this query string array walker. + * + * @param array $query Query string to walk + * @param string $keyPrefix Key prefix (start with '') + * @param callable $prefixer Function used to create a key prefix + * + * @return array + */ + public static function walkQuery(array $query, $keyPrefix, callable $prefixer) + { + $result = []; + foreach ($query as $key => $value) { + if ($keyPrefix) { + $key = $prefixer($key, $keyPrefix); + } + if (is_array($value)) { + $result += self::walkQuery($value, $key, $prefixer); + } elseif (isset($result[$key])) { + $result[$key][] = $value; + } else { + $result[$key] = array($value); + } + } + + return $result; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/QueryParser.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/QueryParser.php new file mode 100644 index 00000000..90727cc6 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/QueryParser.php @@ -0,0 +1,163 @@ +duplicates = false; + $this->numericIndices = true; + $decoder = self::getDecoder($urlEncoding); + + foreach (explode('&', $str) as $kvp) { + + $parts = explode('=', $kvp, 2); + $key = $decoder($parts[0]); + $value = isset($parts[1]) ? $decoder($parts[1]) : null; + + // Special handling needs to be taken for PHP nested array syntax + if (strpos($key, '[') !== false) { + $this->parsePhpValue($key, $value, $result); + continue; + } + + if (!isset($result[$key])) { + $result[$key] = $value; + } else { + $this->duplicates = true; + if (!is_array($result[$key])) { + $result[$key] = [$result[$key]]; + } + $result[$key][] = $value; + } + } + + $query->replace($result); + + if (!$this->numericIndices) { + $query->setAggregator(Query::phpAggregator(false)); + } elseif ($this->duplicates) { + $query->setAggregator(Query::duplicateAggregator()); + } + } + + /** + * Returns a callable that is used to URL decode query keys and values. + * + * @param string|bool $type One of true, false, RFC3986, and RFC1738 + * + * @return callable|string + */ + private static function getDecoder($type) + { + if ($type === true) { + return function ($value) { + return rawurldecode(str_replace('+', ' ', $value)); + }; + } elseif ($type == Query::RFC3986) { + return 'rawurldecode'; + } elseif ($type == Query::RFC1738) { + return 'urldecode'; + } else { + return function ($str) { return $str; }; + } + } + + /** + * Parses a PHP style key value pair. + * + * @param string $key Key to parse (e.g., "foo[a][b]") + * @param string|null $value Value to set + * @param array $result Result to modify by reference + */ + private function parsePhpValue($key, $value, array &$result) + { + $node =& $result; + $keyBuffer = ''; + + for ($i = 0, $t = strlen($key); $i < $t; $i++) { + switch ($key[$i]) { + case '[': + if ($keyBuffer) { + $this->prepareNode($node, $keyBuffer); + $node =& $node[$keyBuffer]; + $keyBuffer = ''; + } + break; + case ']': + $k = $this->cleanKey($node, $keyBuffer); + $this->prepareNode($node, $k); + $node =& $node[$k]; + $keyBuffer = ''; + break; + default: + $keyBuffer .= $key[$i]; + break; + } + } + + if (isset($node)) { + $this->duplicates = true; + $node[] = $value; + } else { + $node = $value; + } + } + + /** + * Prepares a value in the array at the given key. + * + * If the key already exists, the key value is converted into an array. + * + * @param array $node Result node to modify + * @param string $key Key to add or modify in the node + */ + private function prepareNode(&$node, $key) + { + if (!isset($node[$key])) { + $node[$key] = null; + } elseif (!is_array($node[$key])) { + $node[$key] = [$node[$key]]; + } + } + + /** + * Returns the appropriate key based on the node and key. + */ + private function cleanKey($node, $key) + { + if ($key === '') { + $key = $node ? (string) count($node) : 0; + // Found a [] key, so track this to ensure that we disable numeric + // indexing of keys in the resolved query aggregator. + $this->numericIndices = false; + } + + return $key; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/RequestFsm.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/RequestFsm.php new file mode 100644 index 00000000..b37c190d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/RequestFsm.php @@ -0,0 +1,153 @@ +mf = $messageFactory; + $this->maxTransitions = $maxTransitions; + $this->handler = $handler; + } + + /** + * Runs the state machine until a terminal state is entered or the + * optionally supplied $finalState is entered. + * + * @param Transaction $trans Transaction being transitioned. + * + * @throws \Exception if a terminal state throws an exception. + */ + public function __invoke(Transaction $trans) + { + $trans->_transitionCount = 0; + + if (!$trans->state) { + $trans->state = 'before'; + } + + transition: + + if (++$trans->_transitionCount > $this->maxTransitions) { + throw new StateException("Too many state transitions were " + . "encountered ({$trans->_transitionCount}). This likely " + . "means that a combination of event listeners are in an " + . "infinite loop."); + } + + switch ($trans->state) { + case 'before': goto before; + case 'complete': goto complete; + case 'error': goto error; + case 'retry': goto retry; + case 'send': goto send; + case 'end': goto end; + default: throw new StateException("Invalid state: {$trans->state}"); + } + + before: { + try { + $trans->request->getEmitter()->emit('before', new BeforeEvent($trans)); + $trans->state = 'send'; + if ((bool) $trans->response) { + $trans->state = 'complete'; + } + } catch (\Exception $e) { + $trans->state = 'error'; + $trans->exception = $e; + } + goto transition; + } + + complete: { + try { + if ($trans->response instanceof FutureInterface) { + // Futures will have their own end events emitted when + // dereferenced. + return; + } + $trans->state = 'end'; + $trans->response->setEffectiveUrl($trans->request->getUrl()); + $trans->request->getEmitter()->emit('complete', new CompleteEvent($trans)); + } catch (\Exception $e) { + $trans->state = 'error'; + $trans->exception = $e; + } + goto transition; + } + + error: { + try { + // Convert non-request exception to a wrapped exception + $trans->exception = RequestException::wrapException( + $trans->request, $trans->exception + ); + $trans->state = 'end'; + $trans->request->getEmitter()->emit('error', new ErrorEvent($trans)); + // An intercepted request (not retried) transitions to complete + if (!$trans->exception && $trans->state !== 'retry') { + $trans->state = 'complete'; + } + } catch (\Exception $e) { + $trans->state = 'end'; + $trans->exception = $e; + } + goto transition; + } + + retry: { + $trans->retries++; + $trans->response = null; + $trans->exception = null; + $trans->state = 'before'; + goto transition; + } + + send: { + $fn = $this->handler; + $trans->response = FutureResponse::proxy( + $fn(RingBridge::prepareRingRequest($trans)), + function ($value) use ($trans) { + RingBridge::completeRingResponse($trans, $value, $this->mf, $this); + $this($trans); + return $trans->response; + } + ); + return; + } + + end: { + $trans->request->getEmitter()->emit('end', new EndEvent($trans)); + // Throw exceptions in the terminal event if the exception + // was not handled by an "end" event listener. + if ($trans->exception) { + if (!($trans->exception instanceof RequestException)) { + $trans->exception = RequestException::wrapException( + $trans->request, $trans->exception + ); + } + throw $trans->exception; + } + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/RingBridge.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/RingBridge.php new file mode 100644 index 00000000..bc6841d4 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/RingBridge.php @@ -0,0 +1,165 @@ +getConfig()->toArray(); + $url = $request->getUrl(); + // No need to calculate the query string twice (in URL and query). + $qs = ($pos = strpos($url, '?')) ? substr($url, $pos + 1) : null; + + return [ + 'scheme' => $request->getScheme(), + 'http_method' => $request->getMethod(), + 'url' => $url, + 'uri' => $request->getPath(), + 'headers' => $request->getHeaders(), + 'body' => $request->getBody(), + 'version' => $request->getProtocolVersion(), + 'client' => $options, + 'query_string' => $qs, + 'future' => isset($options['future']) ? $options['future'] : false + ]; + } + + /** + * Creates a Ring request from a request object AND prepares the callbacks. + * + * @param Transaction $trans Transaction to update. + * + * @return array Converted Guzzle Ring request. + */ + public static function prepareRingRequest(Transaction $trans) + { + // Clear out the transaction state when initiating. + $trans->exception = null; + $request = self::createRingRequest($trans->request); + + // Emit progress events if any progress listeners are registered. + if ($trans->request->getEmitter()->hasListeners('progress')) { + $emitter = $trans->request->getEmitter(); + $request['client']['progress'] = function ($a, $b, $c, $d) use ($trans, $emitter) { + $emitter->emit('progress', new ProgressEvent($trans, $a, $b, $c, $d)); + }; + } + + return $request; + } + + /** + * Handles the process of processing a response received from a ring + * handler. The created response is added to the transaction, and the + * transaction stat is set appropriately. + * + * @param Transaction $trans Owns request and response. + * @param array $response Ring response array + * @param MessageFactoryInterface $messageFactory Creates response objects. + */ + public static function completeRingResponse( + Transaction $trans, + array $response, + MessageFactoryInterface $messageFactory + ) { + $trans->state = 'complete'; + $trans->transferInfo = isset($response['transfer_stats']) + ? $response['transfer_stats'] : []; + + if (!empty($response['status'])) { + $options = []; + if (isset($response['version'])) { + $options['protocol_version'] = $response['version']; + } + if (isset($response['reason'])) { + $options['reason_phrase'] = $response['reason']; + } + $trans->response = $messageFactory->createResponse( + $response['status'], + isset($response['headers']) ? $response['headers'] : [], + isset($response['body']) ? $response['body'] : null, + $options + ); + if (isset($response['effective_url'])) { + $trans->response->setEffectiveUrl($response['effective_url']); + } + } elseif (empty($response['error'])) { + // When nothing was returned, then we need to add an error. + $response['error'] = self::getNoRingResponseException($trans->request); + } + + if (isset($response['error'])) { + $trans->state = 'error'; + $trans->exception = $response['error']; + } + } + + /** + * Creates a Guzzle request object using a ring request array. + * + * @param array $request Ring request + * + * @return Request + * @throws \InvalidArgumentException for incomplete requests. + */ + public static function fromRingRequest(array $request) + { + $options = []; + if (isset($request['version'])) { + $options['protocol_version'] = $request['version']; + } + + if (!isset($request['http_method'])) { + throw new \InvalidArgumentException('No http_method'); + } + + return new Request( + $request['http_method'], + Core::url($request), + isset($request['headers']) ? $request['headers'] : [], + isset($request['body']) ? Stream::factory($request['body']) : null, + $options + ); + } + + /** + * Get an exception that can be used when a RingPHP handler does not + * populate a response. + * + * @param RequestInterface $request + * + * @return RequestException + */ + public static function getNoRingResponseException(RequestInterface $request) + { + $message = <<cookieJar = $cookieJar ?: new CookieJar(); + } + + public function getEvents() + { + // Fire the cookie plugin complete event before redirecting + return [ + 'before' => ['onBefore'], + 'complete' => ['onComplete', RequestEvents::REDIRECT_RESPONSE + 10] + ]; + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + public function onBefore(BeforeEvent $event) + { + $this->cookieJar->addCookieHeader($event->getRequest()); + } + + public function onComplete(CompleteEvent $event) + { + $this->cookieJar->extractCookies( + $event->getRequest(), + $event->getResponse() + ); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/History.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/History.php new file mode 100644 index 00000000..5cf06119 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/History.php @@ -0,0 +1,172 @@ +limit = $limit; + } + + public function getEvents() + { + return [ + 'complete' => ['onComplete', RequestEvents::EARLY], + 'error' => ['onError', RequestEvents::EARLY], + ]; + } + + /** + * Convert to a string that contains all request and response headers + * + * @return string + */ + public function __toString() + { + $lines = array(); + foreach ($this->transactions as $entry) { + $response = isset($entry['response']) ? $entry['response'] : ''; + $lines[] = '> ' . trim($entry['sent_request']) + . "\n\n< " . trim($response) . "\n"; + } + + return implode("\n", $lines); + } + + public function onComplete(CompleteEvent $event) + { + $this->add($event->getRequest(), $event->getResponse()); + } + + public function onError(ErrorEvent $event) + { + // Only track when no response is present, meaning this didn't ever + // emit a complete event + if (!$event->getResponse()) { + $this->add($event->getRequest()); + } + } + + /** + * Returns an Iterator that yields associative array values where each + * associative array contains the following key value pairs: + * + * - request: Representing the actual request that was received. + * - sent_request: A clone of the request that will not be mutated. + * - response: The response that was received (if available). + * + * @return \Iterator + */ + public function getIterator() + { + return new \ArrayIterator($this->transactions); + } + + /** + * Get all of the requests sent through the plugin. + * + * Requests can be modified after they are logged by the history + * subscriber. By default this method will return the actual request + * instances that were received. Pass true to this method if you wish to + * get copies of the requests that represent the request state when it was + * initially logged by the history subscriber. + * + * @param bool $asSent Set to true to get clones of the requests that have + * not been mutated since the request was received by + * the history subscriber. + * + * @return RequestInterface[] + */ + public function getRequests($asSent = false) + { + return array_map(function ($t) use ($asSent) { + return $asSent ? $t['sent_request'] : $t['request']; + }, $this->transactions); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->transactions); + } + + /** + * Get the last request sent. + * + * Requests can be modified after they are logged by the history + * subscriber. By default this method will return the actual request + * instance that was received. Pass true to this method if you wish to get + * a copy of the request that represents the request state when it was + * initially logged by the history subscriber. + * + * @param bool $asSent Set to true to get a clone of the last request that + * has not been mutated since the request was received + * by the history subscriber. + * + * @return RequestInterface + */ + public function getLastRequest($asSent = false) + { + return $asSent + ? end($this->transactions)['sent_request'] + : end($this->transactions)['request']; + } + + /** + * Get the last response in the history + * + * @return ResponseInterface|null + */ + public function getLastResponse() + { + return end($this->transactions)['response']; + } + + /** + * Clears the history + */ + public function clear() + { + $this->transactions = array(); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * @param ResponseInterface $response Response of the request + */ + private function add( + RequestInterface $request, + ResponseInterface $response = null + ) { + $this->transactions[] = [ + 'request' => $request, + 'sent_request' => clone $request, + 'response' => $response + ]; + if (count($this->transactions) > $this->limit) { + array_shift($this->transactions); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php new file mode 100644 index 00000000..ed9de5bc --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php @@ -0,0 +1,36 @@ + ['onComplete', RequestEvents::VERIFY_RESPONSE]]; + } + + /** + * Throw a RequestException on an HTTP protocol error + * + * @param CompleteEvent $event Emitted event + * @throws RequestException + */ + public function onComplete(CompleteEvent $event) + { + $code = (string) $event->getResponse()->getStatusCode(); + // Throw an exception for an unsuccessful response + if ($code[0] >= 4) { + throw RequestException::create( + $event->getRequest(), + $event->getResponse() + ); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php new file mode 100644 index 00000000..2af4d375 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php @@ -0,0 +1,147 @@ +factory = new MessageFactory(); + $this->readBodies = $readBodies; + $this->addMultiple($items); + } + + public function getEvents() + { + // Fire the event last, after signing + return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST - 10]]; + } + + /** + * @throws \OutOfBoundsException|\Exception + */ + public function onBefore(BeforeEvent $event) + { + if (!$item = array_shift($this->queue)) { + throw new \OutOfBoundsException('Mock queue is empty'); + } elseif ($item instanceof RequestException) { + throw $item; + } + + // Emulate reading a response body + $request = $event->getRequest(); + if ($this->readBodies && $request->getBody()) { + while (!$request->getBody()->eof()) { + $request->getBody()->read(8096); + } + } + + $saveTo = $event->getRequest()->getConfig()->get('save_to'); + + if (null !== $saveTo) { + $body = $item->getBody(); + + if (is_resource($saveTo)) { + fwrite($saveTo, $body); + } elseif (is_string($saveTo)) { + file_put_contents($saveTo, $body); + } elseif ($saveTo instanceof StreamInterface) { + $saveTo->write($body); + } + } + + $event->intercept($item); + } + + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|ResponseInterface $response Response or path to response file + * + * @return self + * @throws \InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (is_string($response)) { + $response = file_exists($response) + ? $this->factory->fromMessage(file_get_contents($response)) + : $this->factory->fromMessage($response); + } elseif (!($response instanceof ResponseInterface)) { + throw new \InvalidArgumentException('Response must a message ' + . 'string, response object, or path to a file'); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Add an exception to the end of the queue + * + * @param RequestException $e Exception to throw when the request is executed + * + * @return self + */ + public function addException(RequestException $e) + { + $this->queue[] = $e; + + return $this; + } + + /** + * Add multiple items to the queue + * + * @param array $items Items to add + */ + public function addMultiple(array $items) + { + foreach ($items as $item) { + if ($item instanceof RequestException) { + $this->addException($item); + } else { + $this->addResponse($item); + } + } + } + + /** + * Clear the queue + */ + public function clearQueue() + { + $this->queue = []; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php new file mode 100644 index 00000000..b5ed4e26 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php @@ -0,0 +1,130 @@ + ['onBefore', RequestEvents::PREPARE_REQUEST]]; + } + + public function onBefore(BeforeEvent $event) + { + $request = $event->getRequest(); + + // Set the appropriate Content-Type for a request if one is not set and + // there are form fields + if (!($body = $request->getBody())) { + return; + } + + $this->addContentLength($request, $body); + + if ($body instanceof AppliesHeadersInterface) { + // Synchronize the body with the request headers + $body->applyRequestHeaders($request); + } elseif (!$request->hasHeader('Content-Type')) { + $this->addContentType($request, $body); + } + + $this->addExpectHeader($request, $body); + } + + private function addContentType( + RequestInterface $request, + StreamInterface $body + ) { + if (!($uri = $body->getMetadata('uri'))) { + return; + } + + // Guess the content-type based on the stream's "uri" metadata value. + // The file extension is used to determine the appropriate mime-type. + if ($contentType = Mimetypes::getInstance()->fromFilename($uri)) { + $request->setHeader('Content-Type', $contentType); + } + } + + private function addContentLength( + RequestInterface $request, + StreamInterface $body + ) { + // Set the Content-Length header if it can be determined, and never + // send a Transfer-Encoding: chunked and Content-Length header in + // the same request. + if ($request->hasHeader('Content-Length')) { + // Remove transfer-encoding if content-length is set. + $request->removeHeader('Transfer-Encoding'); + return; + } + + if ($request->hasHeader('Transfer-Encoding')) { + return; + } + + if (null !== ($size = $body->getSize())) { + $request->setHeader('Content-Length', $size); + $request->removeHeader('Transfer-Encoding'); + } elseif ('1.1' == $request->getProtocolVersion()) { + // Use chunked Transfer-Encoding if there is no determinable + // content-length header and we're using HTTP/1.1. + $request->setHeader('Transfer-Encoding', 'chunked'); + $request->removeHeader('Content-Length'); + } + } + + private function addExpectHeader( + RequestInterface $request, + StreamInterface $body + ) { + // Determine if the Expect header should be used + if ($request->hasHeader('Expect')) { + return; + } + + $expect = $request->getConfig()['expect']; + + // Return if disabled or if you're not using HTTP/1.1 + if ($expect === false || $request->getProtocolVersion() !== '1.1') { + return; + } + + // The expect header is unconditionally enabled + if ($expect === true) { + $request->setHeader('Expect', '100-Continue'); + return; + } + + // By default, send the expect header when the payload is > 1mb + if ($expect === null) { + $expect = 1048576; + } + + // Always add if the body cannot be rewound, the size cannot be + // determined, or the size is greater than the cutoff threshold + $size = $body->getSize(); + if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { + $request->setHeader('Expect', '100-Continue'); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php new file mode 100644 index 00000000..ff992268 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php @@ -0,0 +1,176 @@ + ['onComplete', RequestEvents::REDIRECT_RESPONSE]]; + } + + /** + * Rewind the entity body of the request if needed + * + * @param RequestInterface $redirectRequest + * @throws CouldNotRewindStreamException + */ + public static function rewindEntityBody(RequestInterface $redirectRequest) + { + // Rewind the entity body of the request if needed + if ($body = $redirectRequest->getBody()) { + // Only rewind the body if some of it has been read already, and + // throw an exception if the rewind fails + if ($body->tell() && !$body->seek(0)) { + throw new CouldNotRewindStreamException( + 'Unable to rewind the non-seekable request body after redirecting', + $redirectRequest + ); + } + } + } + + /** + * Called when a request receives a redirect response + * + * @param CompleteEvent $event Event emitted + * @throws TooManyRedirectsException + */ + public function onComplete(CompleteEvent $event) + { + $response = $event->getResponse(); + + if (substr($response->getStatusCode(), 0, 1) != '3' + || !$response->hasHeader('Location') + ) { + return; + } + + $request = $event->getRequest(); + $config = $request->getConfig(); + + // Increment the redirect and initialize the redirect state. + if ($redirectCount = $config['redirect_count']) { + $config['redirect_count'] = ++$redirectCount; + } else { + $config['redirect_scheme'] = $request->getScheme(); + $config['redirect_count'] = $redirectCount = 1; + } + + $max = $config->getPath('redirect/max') ?: 5; + + if ($redirectCount > $max) { + throw new TooManyRedirectsException( + "Will not follow more than {$redirectCount} redirects", + $request + ); + } + + $this->modifyRedirectRequest($request, $response); + $event->retry(); + } + + private function modifyRedirectRequest( + RequestInterface $request, + ResponseInterface $response + ) { + $config = $request->getConfig(); + $protocols = $config->getPath('redirect/protocols') ?: ['http', 'https']; + + // Use a GET request if this is an entity enclosing request and we are + // not forcing RFC compliance, but rather emulating what all browsers + // would do. + $statusCode = $response->getStatusCode(); + if ($statusCode == 303 || + ($statusCode <= 302 && $request->getBody() && !$config->getPath('redirect/strict')) + ) { + $request->setMethod('GET'); + $request->setBody(null); + } + + $previousUrl = $request->getUrl(); + $this->setRedirectUrl($request, $response, $protocols); + $this->rewindEntityBody($request); + + // Add the Referer header if it is told to do so and only + // add the header if we are not redirecting from https to http. + if ($config->getPath('redirect/referer') + && ($request->getScheme() == 'https' || $request->getScheme() == $config['redirect_scheme']) + ) { + $url = Url::fromString($previousUrl); + $url->setUsername(null); + $url->setPassword(null); + $request->setHeader('Referer', (string) $url); + } else { + $request->removeHeader('Referer'); + } + } + + /** + * Set the appropriate URL on the request based on the location header + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param array $protocols + */ + private function setRedirectUrl( + RequestInterface $request, + ResponseInterface $response, + array $protocols + ) { + $location = $response->getHeader('Location'); + $location = Url::fromString($location); + + // Combine location with the original URL if it is not absolute. + if (!$location->isAbsolute()) { + $originalUrl = Url::fromString($request->getUrl()); + // Remove query string parameters and just take what is present on + // the redirect Location header + $originalUrl->getQuery()->clear(); + $location = $originalUrl->combine($location); + } + + // Ensure that the redirect URL is allowed based on the protocols. + if (!in_array($location->getScheme(), $protocols)) { + throw new BadResponseException( + sprintf( + 'Redirect URL, %s, does not use one of the allowed redirect protocols: %s', + $location, + implode(', ', $protocols) + ), + $request, + $response + ); + } + + $request->setUrl($location); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php new file mode 100644 index 00000000..d57c0229 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php @@ -0,0 +1,15 @@ +client = $client; + $this->request = $request; + $this->_future = $future; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/UriTemplate.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/UriTemplate.php new file mode 100644 index 00000000..55dfeb5a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/UriTemplate.php @@ -0,0 +1,241 @@ + array('prefix' => '', 'joiner' => ',', 'query' => false), + '+' => array('prefix' => '', 'joiner' => ',', 'query' => false), + '#' => array('prefix' => '#', 'joiner' => ',', 'query' => false), + '.' => array('prefix' => '.', 'joiner' => '.', 'query' => false), + '/' => array('prefix' => '/', 'joiner' => '/', 'query' => false), + ';' => array('prefix' => ';', 'joiner' => ';', 'query' => true), + '?' => array('prefix' => '?', 'joiner' => '&', 'query' => true), + '&' => array('prefix' => '&', 'joiner' => '&', 'query' => true) + ); + + /** @var array Delimiters */ + private static $delims = array(':', '/', '?', '#', '[', ']', '@', '!', '$', + '&', '\'', '(', ')', '*', '+', ',', ';', '='); + + /** @var array Percent encoded delimiters */ + private static $delimsPct = array('%3A', '%2F', '%3F', '%23', '%5B', '%5D', + '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D'); + + public function expand($template, array $variables) + { + if (false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback( + '/\{([^\}]+)\}/', + [$this, 'expandMatch'], + $this->template + ); + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + $result = array(); + + if (isset(self::$operatorHash[$expression[0]])) { + $result['operator'] = $expression[0]; + $expression = substr($expression, 1); + } else { + $result['operator'] = ''; + } + + foreach (explode(',', $expression) as $value) { + $value = trim($value); + $varspec = array(); + if ($colonPos = strpos($value, ':')) { + $varspec['value'] = substr($value, 0, $colonPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $colonPos + 1); + } elseif (substr($value, -1) == '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $result['values'][] = $varspec; + } + + return $result; + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = array('+' => '%20', '%7e' => '~'); + + $replacements = array(); + $parsed = self::parseExpression($matches[1]); + $prefix = self::$operatorHash[$parsed['operator']]['prefix']; + $joiner = self::$operatorHash[$parsed['operator']]['joiner']; + $useQuery = self::$operatorHash[$parsed['operator']]['query']; + + foreach ($parsed['values'] as $value) { + + if (!isset($this->variables[$value['value']])) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQuery = $useQuery; + $expanded = ''; + + if (is_array($variable)) { + + $isAssoc = $this->isAssoc($variable); + $kvp = array(); + foreach ($variable as $key => $var) { + + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] == '+' || + $parsed['operator'] == '#' + ) { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] == '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested + // structures. + $var = strtr( + http_build_query([$key => $var]), + $rfc1738to3986 + ); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQuery) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQuery = false; + } elseif ($value['modifier'] == '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode + // modifier with an associative array. + $actuallyUseQuery = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the + // explode modifier is not set, then the result must be + // a comma separated list of keys followed by their + // respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + + } else { + if ($value['modifier'] == ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQuery) { + if (!$expanded && $joiner != '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative. + * + * This makes the assumption that input arrays are sequences or hashes. + * This assumption is a tradeoff for accuracy in favor of speed, but it + * should work in almost every case where input is supplied for a URI + * template. + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return $array && array_keys($array)[0] !== 0; + } + + /** + * Removes percent encoding on reserved characters (used with + and # + * modifiers). + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Url.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Url.php new file mode 100644 index 00000000..637f60c2 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Url.php @@ -0,0 +1,595 @@ + 80, 'https' => 443, 'ftp' => 21]; + private static $pathPattern = '/[^a-zA-Z0-9\-\._~!\$&\'\(\)\*\+,;=%:@\/]+|%(?![A-Fa-f0-9]{2})/'; + private static $queryPattern = '/[^a-zA-Z0-9\-\._~!\$\'\(\)\*\+,;%:@\/\?=&]+|%(?![A-Fa-f0-9]{2})/'; + /** @var Query|string Query part of the URL */ + private $query; + + /** + * Factory method to create a new URL from a URL string + * + * @param string $url Full URL used to create a Url object + * + * @return Url + * @throws \InvalidArgumentException + */ + public static function fromString($url) + { + static $defaults = ['scheme' => null, 'host' => null, + 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null]; + + if (false === ($parts = parse_url($url))) { + throw new \InvalidArgumentException('Unable to parse malformed ' + . 'url: ' . $url); + } + + $parts += $defaults; + + // Convert the query string into a Query object + if ($parts['query'] || 0 !== strlen($parts['query'])) { + $parts['query'] = Query::fromString($parts['query']); + } + + return new static($parts['scheme'], $parts['host'], $parts['user'], + $parts['pass'], $parts['port'], $parts['path'], $parts['query'], + $parts['fragment']); + } + + /** + * Build a URL from parse_url parts. The generated URL will be a relative + * URL if a scheme or host are not provided. + * + * @param array $parts Array of parse_url parts + * + * @return string + */ + public static function buildUrl(array $parts) + { + $url = $scheme = ''; + + if (!empty($parts['scheme'])) { + $scheme = $parts['scheme']; + $url .= $scheme . ':'; + } + + if (!empty($parts['host'])) { + $url .= '//'; + if (isset($parts['user'])) { + $url .= $parts['user']; + if (isset($parts['pass'])) { + $url .= ':' . $parts['pass']; + } + $url .= '@'; + } + + $url .= $parts['host']; + + // Only include the port if it is not the default port of the scheme + if (isset($parts['port']) && + (!isset(self::$defaultPorts[$scheme]) || + $parts['port'] != self::$defaultPorts[$scheme]) + ) { + $url .= ':' . $parts['port']; + } + } + + // Add the path component if present + if (isset($parts['path']) && strlen($parts['path'])) { + // Always ensure that the path begins with '/' if set and something + // is before the path + if (!empty($parts['host']) && $parts['path'][0] != '/') { + $url .= '/'; + } + $url .= $parts['path']; + } + + // Add the query string if present + if (isset($parts['query'])) { + $queryStr = (string) $parts['query']; + if ($queryStr || $queryStr === '0') { + $url .= '?' . $queryStr; + } + } + + // Ensure that # is only added to the url if fragment contains anything. + if (isset($parts['fragment'])) { + $url .= '#' . $parts['fragment']; + } + + return $url; + } + + /** + * Create a new URL from URL parts + * + * @param string $scheme Scheme of the URL + * @param string $host Host of the URL + * @param string $username Username of the URL + * @param string $password Password of the URL + * @param int $port Port of the URL + * @param string $path Path of the URL + * @param Query|array|string $query Query string of the URL + * @param string $fragment Fragment of the URL + */ + public function __construct( + $scheme, + $host, + $username = null, + $password = null, + $port = null, + $path = null, + $query = null, + $fragment = null + ) { + $this->scheme = strtolower($scheme); + $this->host = $host; + $this->port = $port; + $this->username = $username; + $this->password = $password; + $this->fragment = $fragment; + + if ($query) { + $this->setQuery($query); + } + + $this->setPath($path); + } + + /** + * Clone the URL + */ + public function __clone() + { + if ($this->query instanceof Query) { + $this->query = clone $this->query; + } + } + + /** + * Returns the URL as a URL string + * + * @return string + */ + public function __toString() + { + return static::buildUrl($this->getParts()); + } + + /** + * Get the parts of the URL as an array + * + * @return array + */ + public function getParts() + { + return array( + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->path, + 'query' => $this->query, + 'fragment' => $this->fragment, + ); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) + * + * @return Url + */ + public function setHost($host) + { + if (strpos($host, ':') === false) { + $this->host = $host; + } else { + list($host, $port) = explode(':', $host); + $this->host = $host; + $this->setPort($port); + } + } + + /** + * Get the host part of the URL + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the scheme part of the URL (http, https, ftp, etc.) + * + * @param string $scheme Scheme to set + */ + public function setScheme($scheme) + { + // Remove the default port if one is specified + if ($this->port + && isset(self::$defaultPorts[$this->scheme]) + && self::$defaultPorts[$this->scheme] == $this->port + ) { + $this->port = null; + } + + $this->scheme = strtolower($scheme); + } + + /** + * Get the scheme part of the URL + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Set the port part of the URL + * + * @param int $port Port to set + */ + public function setPort($port) + { + $this->port = $port; + } + + /** + * Get the port part of the URl. + * + * If no port was set, this method will return the default port for the + * scheme of the URI. + * + * @return int|null + */ + public function getPort() + { + if ($this->port) { + return $this->port; + } elseif (isset(self::$defaultPorts[$this->scheme])) { + return self::$defaultPorts[$this->scheme]; + } + + return null; + } + + /** + * Set the path part of the URL. + * + * The provided URL is URL encoded as necessary. + * + * @param string $path Path string to set + */ + public function setPath($path) + { + $this->path = self::encodePath($path); + } + + /** + * Removes dot segments from a URL + * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 + */ + public function removeDotSegments() + { + static $noopPaths = ['' => true, '/' => true, '*' => true]; + static $ignoreSegments = ['.' => true, '..' => true]; + + if (isset($noopPaths[$this->path])) { + return; + } + + $results = []; + $segments = $this->getPathSegments(); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif (!isset($ignoreSegments[$segment])) { + $results[] = $segment; + } + } + + $newPath = implode('/', $results); + + // Add the leading slash if necessary + if (substr($this->path, 0, 1) === '/' && + substr($newPath, 0, 1) !== '/' + ) { + $newPath = '/' . $newPath; + } + + // Add the trailing slash if necessary + if ($newPath != '/' && isset($ignoreSegments[end($segments)])) { + $newPath .= '/'; + } + + $this->path = $newPath; + } + + /** + * Add a relative path to the currently set path. + * + * @param string $relativePath Relative path to add + */ + public function addPath($relativePath) + { + if ($relativePath != '/' && + is_string($relativePath) && + strlen($relativePath) > 0 + ) { + // Add a leading slash if needed + if ($relativePath[0] !== '/' && + substr($this->path, -1, 1) !== '/' + ) { + $relativePath = '/' . $relativePath; + } + + $this->setPath($this->path . $relativePath); + } + } + + /** + * Get the path part of the URL + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get the path segments of the URL as an array + * + * @return array + */ + public function getPathSegments() + { + return explode('/', $this->path); + } + + /** + * Set the password part of the URL + * + * @param string $password Password to set + */ + public function setPassword($password) + { + $this->password = $password; + } + + /** + * Get the password part of the URL + * + * @return null|string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the username part of the URL + * + * @param string $username Username to set + */ + public function setUsername($username) + { + $this->username = $username; + } + + /** + * Get the username part of the URl + * + * @return null|string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the query part of the URL as a Query object + * + * @return Query + */ + public function getQuery() + { + // Convert the query string to a query object if not already done. + if (!$this->query instanceof Query) { + $this->query = $this->query === null + ? new Query() + : Query::fromString($this->query); + } + + return $this->query; + } + + /** + * Set the query part of the URL. + * + * You may provide a query string as a string and pass $rawString as true + * to provide a query string that is not parsed until a call to getQuery() + * is made. Setting a raw query string will still encode invalid characters + * in a query string. + * + * @param Query|string|array $query Query string value to set. Can + * be a string that will be parsed into a Query object, an array + * of key value pairs, or a Query object. + * @param bool $rawString Set to true when providing a raw query string. + * + * @throws \InvalidArgumentException + */ + public function setQuery($query, $rawString = false) + { + if ($query instanceof Query) { + $this->query = $query; + } elseif (is_string($query)) { + if (!$rawString) { + $this->query = Query::fromString($query); + } else { + // Ensure the query does not have illegal characters. + $this->query = preg_replace_callback( + self::$queryPattern, + [__CLASS__, 'encodeMatch'], + $query + ); + } + + } elseif (is_array($query)) { + $this->query = new Query($query); + } else { + throw new \InvalidArgumentException('Query must be a Query, ' + . 'array, or string. Got ' . Core::describeType($query)); + } + } + + /** + * Get the fragment part of the URL + * + * @return null|string + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * Set the fragment part of the URL + * + * @param string $fragment Fragment to set + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + } + + /** + * Check if this is an absolute URL + * + * @return bool + */ + public function isAbsolute() + { + return $this->scheme && $this->host; + } + + /** + * Combine the URL with another URL and return a new URL instance. + * + * Follows the rules specific in RFC 3986 section 5.4. + * + * @param string $url Relative URL to combine with + * + * @return Url + * @throws \InvalidArgumentException + * @link http://tools.ietf.org/html/rfc3986#section-5.4 + */ + public function combine($url) + { + $url = static::fromString($url); + + // Use the more absolute URL as the base URL + if (!$this->isAbsolute() && $url->isAbsolute()) { + $url = $url->combine($this); + } + + $parts = $url->getParts(); + + // Passing a URL with a scheme overrides everything + if ($parts['scheme']) { + return clone $url; + } + + // Setting a host overrides the entire rest of the URL + if ($parts['host']) { + return new static( + $this->scheme, + $parts['host'], + $parts['user'], + $parts['pass'], + $parts['port'], + $parts['path'], + $parts['query'] instanceof Query + ? clone $parts['query'] + : $parts['query'], + $parts['fragment'] + ); + } + + if (!$parts['path'] && $parts['path'] !== '0') { + // The relative URL has no path, so check if it is just a query + $path = $this->path ?: ''; + $query = $parts['query'] ?: $this->query; + } else { + $query = $parts['query']; + if ($parts['path'][0] == '/' || !$this->path) { + // Overwrite the existing path if the rel path starts with "/" + $path = $parts['path']; + } else { + // If the relative URL does not have a path or the base URL + // path does not end in a "/" then overwrite the existing path + // up to the last "/" + $path = substr($this->path, 0, strrpos($this->path, '/') + 1) . $parts['path']; + } + } + + $result = new self( + $this->scheme, + $this->host, + $this->username, + $this->password, + $this->port, + $path, + $query instanceof Query ? clone $query : $query, + $parts['fragment'] + ); + + if ($path) { + $result->removeDotSegments(); + } + + return $result; + } + + /** + * Encodes the path part of a URL without double-encoding percent-encoded + * key value pairs. + * + * @param string $path Path to encode + * + * @return string + */ + public static function encodePath($path) + { + static $cb = [__CLASS__, 'encodeMatch']; + return preg_replace_callback(self::$pathPattern, $cb, $path); + } + + private static function encodeMatch(array $match) + { + return rawurlencode($match[0]); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Utils.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Utils.php new file mode 100644 index 00000000..17547190 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/src/Utils.php @@ -0,0 +1,215 @@ +expand($template, $variables); + } + + /** + * Wrapper for JSON decode that implements error detection with helpful + * error messages. + * + * @param string $json JSON data to parse + * @param bool $assoc When true, returned objects will be converted + * into associative arrays. + * @param int $depth User specified recursion depth. + * @param int $options Bitmask of JSON decode options. + * + * @return mixed + * @throws \InvalidArgumentException if the JSON cannot be parsed. + * @link http://www.php.net/manual/en/function.json-decode.php + */ + public static function jsonDecode($json, $assoc = false, $depth = 512, $options = 0) + { + if ($json === '' || $json === null) { + return null; + } + + static $jsonErrors = [ + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' + ]; + + $data = \json_decode($json, $assoc, $depth, $options); + + if (JSON_ERROR_NONE !== json_last_error()) { + $last = json_last_error(); + throw new \InvalidArgumentException( + 'Unable to parse JSON data: ' + . (isset($jsonErrors[$last]) + ? $jsonErrors[$last] + : 'Unknown error') + ); + } + + return $data; + } + + /** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ + public static function getDefaultUserAgent() + { + static $defaultAgent = ''; + if (!$defaultAgent) { + $defaultAgent = 'Guzzle/' . ClientInterface::VERSION; + if (extension_loaded('curl')) { + $defaultAgent .= ' curl/' . curl_version()['version']; + } + $defaultAgent .= ' PHP/' . PHP_VERSION; + } + + return $defaultAgent; + } + + /** + * Create a default handler to use based on the environment + * + * @throws \RuntimeException if no viable Handler is available. + */ + public static function getDefaultHandler() + { + $default = $future = null; + + if (extension_loaded('curl')) { + $config = [ + 'select_timeout' => getenv('GUZZLE_CURL_SELECT_TIMEOUT') ?: 1 + ]; + if ($maxHandles = getenv('GUZZLE_CURL_MAX_HANDLES')) { + $config['max_handles'] = $maxHandles; + } + if (function_exists('curl_reset')) { + $default = new CurlHandler(); + $future = new CurlMultiHandler($config); + } else { + $default = new CurlMultiHandler($config); + } + } + + if (ini_get('allow_url_fopen')) { + $default = !$default + ? new StreamHandler() + : Middleware::wrapStreaming($default, new StreamHandler()); + } elseif (!$default) { + throw new \RuntimeException('Guzzle requires cURL, the ' + . 'allow_url_fopen ini setting, or a custom HTTP handler.'); + } + + return $future ? Middleware::wrapFuture($default, $future) : $default; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php new file mode 100644 index 00000000..080d44c0 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php @@ -0,0 +1,58 @@ +assertCount(3, $batch); + $this->assertEquals([$a, $b, $c], $batch->getKeys()); + $this->assertEquals([$hash[$c]], $batch->getFailures()); + $this->assertEquals(['1', '2'], $batch->getSuccessful()); + $this->assertEquals('1', $batch->getResult($a)); + $this->assertNull($batch->getResult(new \stdClass())); + $this->assertTrue(isset($batch[0])); + $this->assertFalse(isset($batch[10])); + $this->assertEquals('1', $batch[0]); + $this->assertEquals('2', $batch[1]); + $this->assertNull($batch[100]); + $this->assertInstanceOf('Exception', $batch[2]); + + $results = iterator_to_array($batch); + $this->assertEquals(['1', '2', $hash[$c]], $results); + } + + /** + * @expectedException \RuntimeException + */ + public function testCannotSetByIndex() + { + $hash = new \SplObjectStorage(); + $batch = new BatchResults($hash); + $batch[10] = 'foo'; + } + + /** + * @expectedException \RuntimeException + */ + public function testCannotUnsetByIndex() + { + $hash = new \SplObjectStorage(); + $batch = new BatchResults($hash); + unset($batch[10]); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/ClientTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/ClientTest.php new file mode 100644 index 00000000..02db3eb9 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/ClientTest.php @@ -0,0 +1,647 @@ +ma = function () { + throw new \RuntimeException('Should not have been called.'); + }; + } + + public function testUsesDefaultDefaultOptions() + { + $client = new Client(); + $this->assertTrue($client->getDefaultOption('allow_redirects')); + $this->assertTrue($client->getDefaultOption('exceptions')); + $this->assertTrue($client->getDefaultOption('verify')); + } + + public function testUsesProvidedDefaultOptions() + { + $client = new Client([ + 'defaults' => [ + 'allow_redirects' => false, + 'query' => ['foo' => 'bar'] + ] + ]); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + $this->assertTrue($client->getDefaultOption('exceptions')); + $this->assertTrue($client->getDefaultOption('verify')); + $this->assertEquals(['foo' => 'bar'], $client->getDefaultOption('query')); + } + + public function testCanSpecifyBaseUrl() + { + $this->assertSame('', (new Client())->getBaseUrl()); + $this->assertEquals('http://foo', (new Client([ + 'base_url' => 'http://foo' + ]))->getBaseUrl()); + } + + public function testCanSpecifyBaseUrlUriTemplate() + { + $client = new Client(['base_url' => ['http://foo.com/{var}/', ['var' => 'baz']]]); + $this->assertEquals('http://foo.com/baz/', $client->getBaseUrl()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesUriTemplateValue() + { + new Client(['base_url' => ['http://foo.com/']]); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyHandler() + { + $client = new Client(['handler' => function () { + throw new \Exception('Foo'); + }]); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyHandlerAsAdapter() + { + $client = new Client(['adapter' => function () { + throw new \Exception('Foo'); + }]); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyMessageFactory() + { + $factory = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') + ->setMethods(['createRequest']) + ->getMockForAbstractClass(); + $factory->expects($this->once()) + ->method('createRequest') + ->will($this->throwException(new \Exception('Foo'))); + $client = new Client(['message_factory' => $factory]); + $client->get(); + } + + public function testCanSpecifyEmitter() + { + $emitter = $this->getMockBuilder('GuzzleHttp\Event\EmitterInterface') + ->setMethods(['listeners']) + ->getMockForAbstractClass(); + $emitter->expects($this->once()) + ->method('listeners') + ->will($this->returnValue('foo')); + + $client = new Client(['emitter' => $emitter]); + $this->assertEquals('foo', $client->getEmitter()->listeners()); + } + + public function testAddsDefaultUserAgentHeaderWithDefaultOptions() + { + $client = new Client(['defaults' => ['allow_redirects' => false]]); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + $this->assertEquals( + ['User-Agent' => Utils::getDefaultUserAgent()], + $client->getDefaultOption('headers') + ); + } + + public function testAddsDefaultUserAgentHeaderWithoutDefaultOptions() + { + $client = new Client(); + $this->assertEquals( + ['User-Agent' => Utils::getDefaultUserAgent()], + $client->getDefaultOption('headers') + ); + } + + private function getRequestClient() + { + $client = $this->getMockBuilder('GuzzleHttp\Client') + ->setMethods(['send']) + ->getMock(); + $client->expects($this->once()) + ->method('send') + ->will($this->returnArgument(0)); + + return $client; + } + + public function requestMethodProvider() + { + return [ + ['GET', false], + ['HEAD', false], + ['DELETE', false], + ['OPTIONS', false], + ['POST', 'foo'], + ['PUT', 'foo'], + ['PATCH', 'foo'] + ]; + } + + /** + * @dataProvider requestMethodProvider + */ + public function testClientProvidesMethodShortcut($method, $body) + { + $client = $this->getRequestClient(); + if ($body) { + $request = $client->{$method}('http://foo.com', [ + 'headers' => ['X-Baz' => 'Bar'], + 'body' => $body, + 'query' => ['a' => 'b'] + ]); + } else { + $request = $client->{$method}('http://foo.com', [ + 'headers' => ['X-Baz' => 'Bar'], + 'query' => ['a' => 'b'] + ]); + } + $this->assertEquals($method, $request->getMethod()); + $this->assertEquals('Bar', $request->getHeader('X-Baz')); + $this->assertEquals('a=b', $request->getQuery()); + if ($body) { + $this->assertEquals($body, $request->getBody()); + } + } + + public function testClientMergesDefaultOptionsWithRequestOptions() + { + $f = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') + ->setMethods(array('createRequest')) + ->getMockForAbstractClass(); + + $o = null; + // Intercept the creation + $f->expects($this->once()) + ->method('createRequest') + ->will($this->returnCallback( + function ($method, $url, array $options = []) use (&$o) { + $o = $options; + return (new MessageFactory())->createRequest($method, $url, $options); + } + )); + + $client = new Client([ + 'message_factory' => $f, + 'defaults' => [ + 'headers' => ['Foo' => 'Bar'], + 'query' => ['baz' => 'bam'], + 'exceptions' => false + ] + ]); + + $request = $client->createRequest('GET', 'http://foo.com?a=b', [ + 'headers' => ['Hi' => 'there', '1' => 'one'], + 'allow_redirects' => false, + 'query' => ['t' => 1] + ]); + + $this->assertFalse($o['allow_redirects']); + $this->assertFalse($o['exceptions']); + $this->assertEquals('Bar', $request->getHeader('Foo')); + $this->assertEquals('there', $request->getHeader('Hi')); + $this->assertEquals('one', $request->getHeader('1')); + $this->assertEquals('a=b&baz=bam&t=1', $request->getQuery()); + } + + public function testClientMergesDefaultHeadersCaseInsensitively() + { + $client = new Client(['defaults' => ['headers' => ['Foo' => 'Bar']]]); + $request = $client->createRequest('GET', 'http://foo.com?a=b', [ + 'headers' => ['foo' => 'custom', 'user-agent' => 'test'] + ]); + $this->assertEquals('test', $request->getHeader('User-Agent')); + $this->assertEquals('custom', $request->getHeader('Foo')); + } + + public function testCanOverrideDefaultOptionWithNull() + { + $client = new Client(['defaults' => ['proxy' => 'invalid!']]); + $request = $client->createRequest('GET', 'http://foo.com?a=b', [ + 'proxy' => null + ]); + $this->assertFalse($request->getConfig()->hasKey('proxy')); + } + + public function testDoesNotOverwriteExistingUA() + { + $client = new Client(['defaults' => [ + 'headers' => ['User-Agent' => 'test'] + ]]); + $this->assertEquals( + ['User-Agent' => 'test'], + $client->getDefaultOption('headers') + ); + } + + public function testUsesBaseUrlWhenNoUrlIsSet() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/baz?bam=bar', + $client->createRequest('GET')->getUrl() + ); + } + + public function testUsesBaseUrlCombinedWithProvidedUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/bar/bam', + $client->createRequest('GET', 'bar/bam')->getUrl() + ); + } + + public function testFalsyPathsAreCombinedWithBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/0', + $client->createRequest('GET', '0')->getUrl() + ); + } + + public function testUsesBaseUrlCombinedWithProvidedUrlViaUriTemplate() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/bar/123', + $client->createRequest('GET', ['bar/{bam}', ['bam' => '123']])->getUrl() + ); + } + + public function testSettingAbsoluteUrlOverridesBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/foo', + $client->createRequest('GET', '/foo')->getUrl() + ); + } + + public function testSettingAbsoluteUriTemplateOverridesBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://goo.com/1', + $client->createRequest( + 'GET', + ['http://goo.com/{bar}', ['bar' => '1']] + )->getUrl() + ); + } + + public function testCanSetRelativeUrlStartingWithHttp() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $this->assertEquals( + 'http://www.foo.com/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } + + /** + * Test that base URLs ending with a slash are resolved as per RFC3986. + * + * @link http://tools.ietf.org/html/rfc3986#section-5.2.3 + */ + public function testMultipleSubdirectoryWithSlash() + { + $client = new Client(['base_url' => 'http://www.foo.com/bar/bam/']); + $this->assertEquals( + 'http://www.foo.com/bar/bam/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } + + /** + * Test that base URLs ending without a slash are resolved as per RFC3986. + * + * @link http://tools.ietf.org/html/rfc3986#section-5.2.3 + */ + public function testMultipleSubdirectoryNoSlash() + { + $client = new Client(['base_url' => 'http://www.foo.com/bar/bam']); + $this->assertEquals( + 'http://www.foo.com/bar/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } + + public function testClientSendsRequests() + { + $mock = new MockHandler(['status' => 200, 'headers' => []]); + $client = new Client(['handler' => $mock]); + $response = $client->get('http://test.com'); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('http://test.com', $response->getEffectiveUrl()); + } + + public function testSendingRequestCanBeIntercepted() + { + $response = new Response(200); + $client = new Client(['handler' => $this->ma]); + $client->getEmitter()->on( + 'before', + function (BeforeEvent $e) use ($response) { + $e->intercept($response); + } + ); + $this->assertSame($response, $client->get('http://test.com')); + $this->assertEquals('http://test.com', $response->getEffectiveUrl()); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage Argument 1 passed to GuzzleHttp\Message\FutureResponse::proxy() must implement interface GuzzleHttp\Ring\Future\FutureInterface + */ + public function testEnsuresResponseIsPresentAfterSending() + { + $handler = function () {}; + $client = new Client(['handler' => $handler]); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage Waiting did not resolve future + */ + public function testEnsuresResponseIsPresentAfterDereferencing() + { + $deferred = new Deferred(); + $handler = new MockHandler(function () use ($deferred) { + return new FutureArray( + $deferred->promise(), + function () {} + ); + }); + $client = new Client(['handler' => $handler]); + $response = $client->get('http://httpbin.org'); + $response->wait(); + } + + public function testClientHandlesErrorsDuringBeforeSend() + { + $client = new Client(); + $client->getEmitter()->on('before', function ($e) { + throw new \Exception('foo'); + }); + $client->getEmitter()->on('error', function (ErrorEvent $e) { + $e->intercept(new Response(200)); + }); + $this->assertEquals( + 200, + $client->get('http://test.com')->getStatusCode() + ); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage foo + */ + public function testClientHandlesErrorsDuringBeforeSendAndThrowsIfUnhandled() + { + $client = new Client(); + $client->getEmitter()->on('before', function (BeforeEvent $e) { + throw new RequestException('foo', $e->getRequest()); + }); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage foo + */ + public function testClientWrapsExceptions() + { + $client = new Client(); + $client->getEmitter()->on('before', function (BeforeEvent $e) { + throw new \Exception('foo'); + }); + $client->get('http://httpbin.org'); + } + + public function testCanInjectResponseForFutureError() + { + $calledFuture = false; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred, &$calledFuture) { + $calledFuture = true; + $deferred->resolve(['error' => new \Exception('Noo!')]); + } + ); + $mock = new MockHandler($future); + $client = new Client(['handler' => $mock]); + $called = 0; + $response = $client->get('http://localhost:123/foo', [ + 'future' => true, + 'events' => [ + 'error' => function (ErrorEvent $e) use (&$called) { + $called++; + $e->intercept(new Response(200)); + } + ] + ]); + $this->assertEquals(0, $called); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $response); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertTrue($calledFuture); + $this->assertEquals(1, $called); + } + + public function testCanReturnFutureResults() + { + $called = false; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred, &$called) { + $called = true; + $deferred->resolve(['status' => 201, 'headers' => []]); + } + ); + $mock = new MockHandler($future); + $client = new Client(['handler' => $mock]); + $response = $client->get('http://localhost:123/foo', ['future' => true]); + $this->assertFalse($called); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $response); + $this->assertEquals(201, $response->getStatusCode()); + $this->assertTrue($called); + } + + public function testThrowsExceptionsWhenDereferenced() + { + $calledFuture = false; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred, &$calledFuture) { + $calledFuture = true; + $deferred->resolve(['error' => new \Exception('Noop!')]); + } + ); + $client = new Client(['handler' => new MockHandler($future)]); + try { + $res = $client->get('http://localhost:123/foo', ['future' => true]); + $res->wait(); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertEquals(1, $calledFuture); + } + } + + /** + * @expectedExceptionMessage Noo! + * @expectedException \GuzzleHttp\Exception\RequestException + */ + public function testThrowsExceptionsSynchronously() + { + $client = new Client([ + 'handler' => new MockHandler(['error' => new \Exception('Noo!')]) + ]); + $client->get('http://localhost:123/foo'); + } + + public function testCanSetDefaultValues() + { + $client = new Client(['foo' => 'bar']); + $client->setDefaultOption('headers/foo', 'bar'); + $this->assertNull($client->getDefaultOption('foo')); + $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); + } + + public function testSendsAllInParallel() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(200), + new Response(201), + new Response(202), + ])); + $history = new History(); + $client->getEmitter()->attach($history); + + $requests = [ + $client->createRequest('GET', 'http://test.com'), + $client->createRequest('POST', 'http://test.com'), + $client->createRequest('PUT', 'http://test.com') + ]; + + $client->sendAll($requests); + $requests = array_map(function($r) { + return $r->getMethod(); + }, $history->getRequests()); + $this->assertContains('GET', $requests); + $this->assertContains('POST', $requests); + $this->assertContains('PUT', $requests); + } + + public function testCanDisableAuthPerRequest() + { + $client = new Client(['defaults' => ['auth' => 'foo']]); + $request = $client->createRequest('GET', 'http://test.com'); + $this->assertEquals('foo', $request->getConfig()['auth']); + $request = $client->createRequest('GET', 'http://test.com', ['auth' => null]); + $this->assertFalse($request->getConfig()->hasKey('auth')); + } + + public function testUsesProxyEnvironmentVariables() + { + $http = getenv('HTTP_PROXY'); + $https = getenv('HTTPS_PROXY'); + + $client = new Client(); + $this->assertNull($client->getDefaultOption('proxy')); + + putenv('HTTP_PROXY=127.0.0.1'); + $client = new Client(); + $this->assertEquals( + ['http' => '127.0.0.1'], + $client->getDefaultOption('proxy') + ); + + putenv('HTTPS_PROXY=127.0.0.2'); + $client = new Client(); + $this->assertEquals( + ['http' => '127.0.0.1', 'https' => '127.0.0.2'], + $client->getDefaultOption('proxy') + ); + + putenv("HTTP_PROXY=$http"); + putenv("HTTPS_PROXY=$https"); + } + + public function testReturnsFutureForErrorWhenRequested() + { + $client = new Client(['handler' => new MockHandler(['status' => 404])]); + $request = $client->createRequest('GET', 'http://localhost:123/foo', [ + 'future' => true + ]); + $res = $client->send($request); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res); + try { + $res->wait(); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertContains('404', $e->getMessage()); + } + } + + public function testReturnsFutureForResponseWhenRequested() + { + $client = new Client(['handler' => new MockHandler(['status' => 200])]); + $request = $client->createRequest('GET', 'http://localhost:123/foo', [ + 'future' => true + ]); + $res = $client->send($request); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res); + $this->assertEquals(200, $res->getStatusCode()); + } + + public function testCanUseUrlWithCustomQuery() + { + $client = new Client(); + $url = Url::fromString('http://foo.com/bar'); + $query = new Query(['baz' => '123%20']); + $query->setEncodingType(false); + $url->setQuery($query); + $r = $client->createRequest('GET', $url); + $this->assertEquals('http://foo.com/bar?baz=123%20', $r->getUrl()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/CollectionTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/CollectionTest.php new file mode 100644 index 00000000..8c532aaf --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/CollectionTest.php @@ -0,0 +1,416 @@ +coll = new Collection(); + } + + public function testConstructorCanBeCalledWithNoParams() + { + $this->coll = new Collection(); + $p = $this->coll->toArray(); + $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); + } + + public function testConstructorCanBeCalledWithParams() + { + $testData = array( + 'test' => 'value', + 'test_2' => 'value2' + ); + $this->coll = new Collection($testData); + $this->assertEquals($this->coll->toArray(), $testData); + $this->assertEquals($this->coll->toArray(), $this->coll->toArray()); + } + + public function testImplementsIteratorAggregate() + { + $this->coll->set('key', 'value'); + $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); + $this->assertEquals(1, count($this->coll)); + $total = 0; + foreach ($this->coll as $key => $value) { + $this->assertEquals('key', $key); + $this->assertEquals('value', $value); + $total++; + } + $this->assertEquals(1, $total); + } + + public function testCanAddValuesToExistingKeysByUsingArray() + { + $this->coll->add('test', 'value1'); + $this->assertEquals($this->coll->toArray(), array('test' => 'value1')); + $this->coll->add('test', 'value2'); + $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2'))); + $this->coll->add('test', 'value3'); + $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2', 'value3'))); + } + + public function testHandlesMergingInDisparateDataSources() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + $this->coll->merge($params); + $this->assertEquals($this->coll->toArray(), $params); + $this->coll->merge(new Collection(['test4' => 'hi'])); + $this->assertEquals( + $this->coll->toArray(), + $params + ['test4' => 'hi'] + ); + } + + public function testCanClearAllDataOrSpecificKeys() + { + $this->coll->merge(array( + 'test' => 'value1', + 'test2' => 'value2' + )); + + // Clear a specific parameter by name + $this->coll->remove('test'); + + $this->assertEquals($this->coll->toArray(), array( + 'test2' => 'value2' + )); + + // Clear all parameters + $this->coll->clear(); + + $this->assertEquals($this->coll->toArray(), array()); + } + + public function testProvidesKeys() + { + $this->assertEquals(array(), $this->coll->getKeys()); + $this->coll->merge(array( + 'test1' => 'value1', + 'test2' => 'value2' + )); + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + // Returns the cached array previously returned + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + $this->coll->remove('test1'); + $this->assertEquals(array('test2'), $this->coll->getKeys()); + $this->coll->add('test3', 'value3'); + $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); + } + + public function testChecksIfHasKey() + { + $this->assertFalse($this->coll->hasKey('test')); + $this->coll->add('test', 'value'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->coll->add('test2', 'value2'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->assertEquals(true, $this->coll->hasKey('test2')); + $this->assertFalse($this->coll->hasKey('testing')); + $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); + } + + public function testChecksIfHasValue() + { + $this->assertFalse($this->coll->hasValue('value')); + $this->coll->add('test', 'value'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->coll->add('test2', 'value2'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->assertEquals('test2', $this->coll->hasValue('value2')); + $this->assertFalse($this->coll->hasValue('val')); + } + + public function testImplementsCount() + { + $data = new Collection(); + $this->assertEquals(0, $data->count()); + $data->add('key', 'value'); + $this->assertEquals(1, count($data)); + $data->add('key', 'value2'); + $this->assertEquals(1, count($data)); + $data->add('key_2', 'value3'); + $this->assertEquals(2, count($data)); + } + + public function testAddParamsByMerging() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + + // Add some parameters + $this->coll->merge($params); + + // Add more parameters by merging them in + $this->coll->merge(array( + 'test' => 'another', + 'different_key' => 'new value' + )); + + $this->assertEquals(array( + 'test' => array('value1', 'another'), + 'test2' => 'value2', + 'test3' => array('value3', 'value4'), + 'different_key' => 'new value' + ), $this->coll->toArray()); + } + + public function testAllowsFunctionalFilter() + { + $this->coll->merge(array( + 'fruit' => 'apple', + 'number' => 'ten', + 'prepositions' => array('about', 'above', 'across', 'after'), + 'same_number' => 'ten' + )); + + $filtered = $this->coll->filter(function ($key, $value) { + return $value == 'ten'; + }); + + $this->assertNotSame($filtered, $this->coll); + + $this->assertEquals(array( + 'number' => 'ten', + 'same_number' => 'ten' + ), $filtered->toArray()); + } + + public function testAllowsFunctionalMapping() + { + $this->coll->merge(array( + 'number_1' => 1, + 'number_2' => 2, + 'number_3' => 3 + )); + + $mapped = $this->coll->map(function ($key, $value) { + return $value * $value; + }); + + $this->assertNotSame($mapped, $this->coll); + + $this->assertEquals(array( + 'number_1' => 1, + 'number_2' => 4, + 'number_3' => 9 + ), $mapped->toArray()); + } + + public function testImplementsArrayAccess() + { + $this->coll->merge(array( + 'k1' => 'v1', + 'k2' => 'v2' + )); + + $this->assertTrue($this->coll->offsetExists('k1')); + $this->assertFalse($this->coll->offsetExists('Krull')); + + $this->coll->offsetSet('k3', 'v3'); + $this->assertEquals('v3', $this->coll->offsetGet('k3')); + $this->assertEquals('v3', $this->coll->get('k3')); + + $this->coll->offsetUnset('k1'); + $this->assertFalse($this->coll->offsetExists('k1')); + } + + public function testCanReplaceAllData() + { + $this->coll->replace(array('a' => '123')); + $this->assertEquals(array('a' => '123'), $this->coll->toArray()); + } + + public function testPreparesFromConfig() + { + $c = Collection::fromConfig(array( + 'a' => '123', + 'base_url' => 'http://www.test.com/' + ), array( + 'a' => 'xyz', + 'b' => 'lol' + ), array('a')); + + $this->assertInstanceOf('GuzzleHttp\Collection', $c); + $this->assertEquals(array( + 'a' => '123', + 'b' => 'lol', + 'base_url' => 'http://www.test.com/' + ), $c->toArray()); + + try { + Collection::fromConfig(array(), array(), array('a')); + $this->fail('Exception not throw when missing config'); + } catch (\InvalidArgumentException $e) { + } + } + + function falseyDataProvider() + { + return array( + array(false, false), + array(null, null), + array('', ''), + array(array(), array()), + array(0, 0), + ); + } + + /** + * @dataProvider falseyDataProvider + */ + public function testReturnsCorrectData($a, $b) + { + $c = new Collection(array('value' => $a)); + $this->assertSame($b, $c->get('value')); + } + + public function testRetrievesNestedKeysUsingPath() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar' + ) + ) + ); + $collection = new Collection($data); + $this->assertEquals('bar', $collection->getPath('foo')); + $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); + $this->assertNull($collection->getPath('wewewf')); + $this->assertNull($collection->getPath('baz/mesa/jar/jar')); + } + + public function testFalseyKeysStillDescend() + { + $collection = new Collection(array( + '0' => array( + 'a' => 'jar' + ), + 1 => 'other' + )); + $this->assertEquals('jar', $collection->getPath('0/a')); + $this->assertEquals('other', $collection->getPath('1')); + } + + public function getPathProvider() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c') + ), + 'bar' => array( + 'baz' => 'bam', + 'array' => array('d', 'e', 'f') + ) + ), + 'bam' => array( + array('foo' => 1), + array('foo' => 2), + array('array' => array('h', 'i')) + ) + ); + $c = new Collection($data); + + return array( + // Simple path selectors + array($c, 'foo', 'bar'), + array($c, 'baz', $data['baz']), + array($c, 'bam', $data['bam']), + array($c, 'baz/mesa', $data['baz']['mesa']), + array($c, 'baz/mesa/jar', 'jar'), + // Does not barf on missing keys + array($c, 'fefwfw', null), + array($c, 'baz/mesa/array', $data['baz']['mesa']['array']) + ); + } + + /** + * @dataProvider getPathProvider + */ + public function testGetPath(Collection $c, $path, $expected, $separator = '/') + { + $this->assertEquals($expected, $c->getPath($path, $separator)); + } + + public function testOverridesSettings() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $c->overwriteWith(array('foo' => 10, 'bar' => 300)); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testOverwriteWithCollection() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testOverwriteWithTraversable() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b->getIterator()); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testCanSetNestedPathValueThatDoesNotExist() + { + $c = new Collection(array()); + $c->setPath('foo/bar/baz/123', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); + } + + public function testCanSetNestedPathValueThatExists() + { + $c = new Collection(array('foo' => array('bar' => 'test'))); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \RuntimeException + */ + public function testVerifiesNestedPathIsValidAtExactLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \RuntimeException + */ + public function testVerifiesThatNestedPathIsValidAtAnyLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar/baz', 'test'); + } + + public function testCanAppendToNestedPathValues() + { + $c = new Collection(); + $c->setPath('foo/bar/[]', 'a'); + $c->setPath('foo/bar/[]', 'b'); + $this->assertEquals(['a', 'b'], $c['foo']['bar']); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php new file mode 100644 index 00000000..1360419d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php @@ -0,0 +1,339 @@ +jar = new CookieJar(); + } + + protected function getTestCookies() + { + return [ + new SetCookie(['Name' => 'foo', 'Value' => 'bar', 'Domain' => 'foo.com', 'Path' => '/', 'Discard' => true]), + new SetCookie(['Name' => 'test', 'Value' => '123', 'Domain' => 'baz.com', 'Path' => '/foo', 'Expires' => 2]), + new SetCookie(['Name' => 'you', 'Value' => '123', 'Domain' => 'bar.com', 'Path' => '/boo', 'Expires' => time() + 1000]) + ]; + } + + public function testQuotesBadCookieValues() + { + $this->assertEquals('foo', CookieJar::getCookieValue('foo')); + $this->assertEquals('"foo,bar"', CookieJar::getCookieValue('foo,bar')); + } + + public function testCreatesFromArray() + { + $jar = CookieJar::fromArray([ + 'foo' => 'bar', + 'baz' => 'bam' + ], 'example.com'); + $this->assertCount(2, $jar); + } + + /** + * Provides test data for cookie cookieJar retrieval + */ + public function getCookiesDataProvider() + { + return [ + [['foo', 'baz', 'test', 'muppet', 'googoo'], '', '', '', false], + [['foo', 'baz', 'muppet', 'googoo'], '', '', '', true], + [['googoo'], 'www.example.com', '', '', false], + [['muppet', 'googoo'], 'test.y.example.com', '', '', false], + [['foo', 'baz'], 'example.com', '', '', false], + [['muppet'], 'x.y.example.com', '/acme/', '', false], + [['muppet'], 'x.y.example.com', '/acme/test/', '', false], + [['googoo'], 'x.y.example.com', '/test/acme/test/', '', false], + [['foo', 'baz'], 'example.com', '', '', false], + [['baz'], 'example.com', '', 'baz', false], + ]; + } + + public function testStoresAndRetrievesCookies() + { + $cookies = $this->getTestCookies(); + foreach ($cookies as $cookie) { + $this->assertTrue($this->jar->setCookie($cookie)); + } + + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(3, count($this->jar->getIterator())); + $this->assertEquals($cookies, $this->jar->getIterator()->getArrayCopy()); + } + + public function testRemovesTemporaryCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->setCookie($cookie); + } + $this->jar->clearSessionCookies(); + $this->assertEquals( + [$cookies[1], $cookies[2]], + $this->jar->getIterator()->getArrayCopy() + ); + } + + public function testRemovesSelectively() + { + foreach ($this->getTestCookies() as $cookie) { + $this->jar->setCookie($cookie); + } + + // Remove foo.com cookies + $this->jar->clear('foo.com'); + $this->assertEquals(2, count($this->jar)); + // Try again, removing no further cookies + $this->jar->clear('foo.com'); + $this->assertEquals(2, count($this->jar)); + + // Remove bar.com cookies with path of /boo + $this->jar->clear('bar.com', '/boo'); + $this->assertEquals(1, count($this->jar)); + + // Remove cookie by name + $this->jar->clear(null, null, 'test'); + $this->assertEquals(0, count($this->jar)); + } + + public function testDoesNotAddIncompleteCookies() + { + $this->assertEquals(false, $this->jar->setCookie(new SetCookie())); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo' + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => false + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => true + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com' + )))); + } + + public function testDoesAddValidCookies() + { + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => 0 + )))); + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => 0.0 + )))); + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => '0' + )))); + } + + public function testOverwritesCookiesThatAreOlderOrDiscardable() + { + $t = time() + 1000; + $data = array( + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => '.example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true, + 'Discard' => true, + 'Expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $data['Discard'] = false; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertEquals(false, $c[0]->getDiscard()); + + // Make sure it doesn't duplicate the cookie + $this->jar->setCookie(new SetCookie($data)); + $this->assertEquals(1, count($this->jar)); + + // Make sure the more future-ful expiration date supersede the other + $data['Expires'] = time() + 2000; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertNotEquals($t, $c[0]->getExpires()); + } + + public function testOverwritesCookiesThatHaveChanged() + { + $t = time() + 1000; + $data = array( + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => '.example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true, + 'Discard' => true, + 'Expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + + $data['Value'] = 'boo'; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + // Changing the value plus a parameter also must overwrite the existing one + $data['Value'] = 'zoo'; + $data['Secure'] = false; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertEquals('zoo', $c[0]->getValue()); + } + + public function testAddsCookiesFromResponseWithRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" + )); + $request = new Request('GET', 'http://www.example.com'); + $this->jar->extractCookies($request, $response); + $this->assertEquals(1, count($this->jar)); + } + + public function getMatchingCookiesDataProvider() + { + return array( + array('https://example.com', 'foo=bar; baz=foobar'), + array('http://example.com', ''), + array('https://example.com:8912', 'foo=bar; baz=foobar'), + array('https://foo.example.com', 'foo=bar; baz=foobar'), + array('http://foo.example.com/test/acme/', 'googoo=gaga') + ); + } + + /** + * @dataProvider getMatchingCookiesDataProvider + */ + public function testReturnsCookiesMatchingRequests($url, $cookies) + { + $bag = [ + new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true + ]), + new SetCookie([ + 'Name' => 'baz', + 'Value' => 'foobar', + 'Domain' => 'example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true + ]), + new SetCookie([ + 'Name' => 'test', + 'Value' => '123', + 'Domain' => 'www.foobar.com', + 'Path' => '/path/', + 'Discard' => true + ]), + new SetCookie([ + 'Name' => 'muppet', + 'Value' => 'cookie_monster', + 'Domain' => '.y.example.com', + 'Path' => '/acme/', + 'Expires' => time() + 86400 + ]), + new SetCookie([ + 'Name' => 'googoo', + 'Value' => 'gaga', + 'Domain' => '.example.com', + 'Path' => '/test/acme/', + 'Max-Age' => 1500 + ]) + ]; + + foreach ($bag as $cookie) { + $this->jar->setCookie($cookie); + } + + $request = new Request('GET', $url); + $this->jar->addCookieHeader($request); + $this->assertEquals($cookies, $request->getHeader('Cookie')); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Invalid cookie: Cookie name must not cannot invalid characters: + */ + public function testThrowsExceptionWithStrictMode() + { + $a = new CookieJar(true); + $a->setCookie(new SetCookie(['Name' => "abc\n", 'Value' => 'foo', 'Domain' => 'bar'])); + } + + public function testDeletesCookiesByName() + { + $cookies = $this->getTestCookies(); + $cookies[] = new SetCookie([ + 'Name' => 'other', + 'Value' => '123', + 'Domain' => 'bar.com', + 'Path' => '/boo', + 'Expires' => time() + 1000 + ]); + $jar = new CookieJar(); + foreach ($cookies as $cookie) { + $jar->setCookie($cookie); + } + $this->assertCount(4, $jar); + $jar->clear('bar.com', '/boo', 'other'); + $this->assertCount(3, $jar); + $names = array_map(function (SetCookie $c) { + return $c->getName(); + }, $jar->getIterator()->getArrayCopy()); + $this->assertEquals(['foo', 'test', 'you'], $names); + } + + public function testCanConvertToAndLoadFromArray() + { + $jar = new CookieJar(true); + foreach ($this->getTestCookies() as $cookie) { + $jar->setCookie($cookie); + } + $this->assertCount(3, $jar); + $arr = $jar->toArray(); + $this->assertCount(3, $arr); + $newCookieJar = new CookieJar(false, $arr); + $this->assertCount(3, $newCookieJar); + $this->assertSame($jar->toArray(), $newCookieJar->toArray()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php new file mode 100644 index 00000000..1d113371 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php @@ -0,0 +1,71 @@ +file = tempnam('/tmp', 'file-cookies'); + } + + /** + * @expectedException \RuntimeException + */ + public function testValidatesCookieFile() + { + file_put_contents($this->file, 'true'); + new FileCookieJar($this->file); + } + + public function testLoadsFromFileFile() + { + $jar = new FileCookieJar($this->file); + $this->assertEquals([], $jar->getIterator()->getArrayCopy()); + unlink($this->file); + } + + public function testPersistsToFileFile() + { + $jar = new FileCookieJar($this->file); + $jar->setCookie(new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'baz', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'boo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + ])); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the file + $contents = file_get_contents($this->file); + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new FileCookieJar($this->file); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unlink($this->file); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php new file mode 100644 index 00000000..ccc6d4ee --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php @@ -0,0 +1,76 @@ +sessionVar = 'sessionKey'; + + if (!isset($_SESSION)) { + $_SESSION = array(); + } + } + + /** + * @expectedException \RuntimeException + */ + public function testValidatesCookieSession() + { + $_SESSION[$this->sessionVar] = 'true'; + new SessionCookieJar($this->sessionVar); + } + + public function testLoadsFromSession() + { + $jar = new SessionCookieJar($this->sessionVar); + $this->assertEquals([], $jar->getIterator()->getArrayCopy()); + unset($_SESSION[$this->sessionVar]); + } + + public function testPersistsToSession() + { + $jar = new SessionCookieJar($this->sessionVar); + $jar->setCookie(new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'baz', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'boo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + ])); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the sessionVar in $_SESSION + $contents = $_SESSION[$this->sessionVar]; + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new SessionCookieJar($this->sessionVar); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unset($_SESSION[$this->sessionVar]); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php new file mode 100644 index 00000000..3ddd0820 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php @@ -0,0 +1,364 @@ +assertEquals('/', $cookie->getPath()); + } + + public function testConvertsDateTimeMaxAgeToUnixTimestamp() + { + $cookie = new SetCookie(['Expires' => 'November 20, 1984']); + $this->assertInternalType('integer', $cookie->getExpires()); + } + + public function testAddsExpiresBasedOnMaxAge() + { + $t = time(); + $cookie = new SetCookie(['Max-Age' => 100]); + $this->assertEquals($t + 100, $cookie->getExpires()); + } + + public function testHoldsValues() + { + $t = time(); + $data = array( + 'Name' => 'foo', + 'Value' => 'baz', + 'Path' => '/bar', + 'Domain' => 'baz.com', + 'Expires' => $t, + 'Max-Age' => 100, + 'Secure' => true, + 'Discard' => true, + 'HttpOnly' => true, + 'foo' => 'baz', + 'bar' => 'bam' + ); + + $cookie = new SetCookie($data); + $this->assertEquals($data, $cookie->toArray()); + + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('baz', $cookie->getValue()); + $this->assertEquals('baz.com', $cookie->getDomain()); + $this->assertEquals('/bar', $cookie->getPath()); + $this->assertEquals($t, $cookie->getExpires()); + $this->assertEquals(100, $cookie->getMaxAge()); + $this->assertTrue($cookie->getSecure()); + $this->assertTrue($cookie->getDiscard()); + $this->assertTrue($cookie->getHttpOnly()); + $this->assertEquals('baz', $cookie->toArray()['foo']); + $this->assertEquals('bam', $cookie->toArray()['bar']); + + $cookie->setName('a'); + $cookie->setValue('b'); + $cookie->setPath('c'); + $cookie->setDomain('bar.com'); + $cookie->setExpires(10); + $cookie->setMaxAge(200); + $cookie->setSecure(false); + $cookie->setHttpOnly(false); + $cookie->setDiscard(false); + + $this->assertEquals('a', $cookie->getName()); + $this->assertEquals('b', $cookie->getValue()); + $this->assertEquals('c', $cookie->getPath()); + $this->assertEquals('bar.com', $cookie->getDomain()); + $this->assertEquals(10, $cookie->getExpires()); + $this->assertEquals(200, $cookie->getMaxAge()); + $this->assertFalse($cookie->getSecure()); + $this->assertFalse($cookie->getDiscard()); + $this->assertFalse($cookie->getHttpOnly()); + } + + public function testDeterminesIfExpired() + { + $c = new SetCookie(); + $c->setExpires(10); + $this->assertTrue($c->isExpired()); + $c->setExpires(time() + 10000); + $this->assertFalse($c->isExpired()); + } + + public function testMatchesDomain() + { + $cookie = new SetCookie(); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('baz.com'); + $this->assertTrue($cookie->matchesDomain('baz.com')); + $this->assertFalse($cookie->matchesDomain('bar.com')); + + $cookie->setDomain('.baz.com'); + $this->assertTrue($cookie->matchesDomain('.baz.com')); + $this->assertTrue($cookie->matchesDomain('foo.baz.com')); + $this->assertFalse($cookie->matchesDomain('baz.bar.com')); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('.com.'); + $this->assertFalse($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.local'); + $this->assertTrue($cookie->matchesDomain('example.local')); + } + + public function testMatchesPath() + { + $cookie = new SetCookie(); + $this->assertTrue($cookie->matchesPath('/foo')); + + $cookie->setPath('/foo'); + $this->assertTrue($cookie->matchesPath('/foo')); + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/bar')); + } + + public function cookieValidateProvider() + { + return array( + array('foo', 'baz', 'bar', true), + array('0', '0', '0', true), + array('', 'baz', 'bar', 'The cookie name must not be empty'), + array('foo', '', 'bar', 'The cookie value must not be empty'), + array('foo', 'baz', '', 'The cookie domain must not be empty'), + array("foo\r", 'baz', '0', 'Cookie name must not cannot invalid characters: =,; \t\r\n\013\014'), + ); + } + + /** + * @dataProvider cookieValidateProvider + */ + public function testValidatesCookies($name, $value, $domain, $result) + { + $cookie = new SetCookie(array( + 'Name' => $name, + 'Value' => $value, + 'Domain' => $domain + )); + $this->assertSame($result, $cookie->validate()); + } + + public function testDoesNotMatchIp() + { + $cookie = new SetCookie(['Domain' => '192.168.16.']); + $this->assertFalse($cookie->matchesDomain('192.168.16.121')); + } + + public function testConvertsToString() + { + $t = 1382916008; + $cookie = new SetCookie([ + 'Name' => 'test', + 'Value' => '123', + 'Domain' => 'foo.com', + 'Expires' => $t, + 'Path' => '/abc', + 'HttpOnly' => true, + 'Secure' => true + ]); + $this->assertEquals( + 'test=123; Domain=foo.com; Path=/abc; Expires=Sun, 27 Oct 2013 23:20:08 GMT; Secure; HttpOnly', + (string) $cookie + ); + } + + /** + * Provides the parsed information from a cookie + * + * @return array + */ + public function cookieParserDataProvider() + { + return array( + array( + 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/";', + array( + 'Domain' => 'allseeing-i.com', + 'Path' => '/', + 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c', + 'Max-Age' => NULL, + 'Expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', + 'Secure' => NULL, + 'Discard' => NULL, + 'Name' => 'ASIHTTPRequestTestCookie', + 'Value' => 'This+is+the+value', + 'HttpOnly' => false + ) + ), + array('', []), + array('foo', []), + // Test setting a blank value for a cookie + array(array( + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), + array( + 'Name' => 'foo', + 'Value' => '', + 'Discard' => null, + 'Domain' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + // Test setting a value and removing quotes + array(array( + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), + array( + 'Name' => 'foo', + 'Value' => '1', + 'Discard' => null, + 'Domain' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php + array( + 'justacookie=foo; domain=example.com', + array( + 'Name' => 'justacookie', + 'Value' => 'foo', + 'Domain' => 'example.com', + 'Discard' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + array( + 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', + array( + 'Name' => 'expires', + 'Value' => 'tomorrow', + 'Domain' => '.example.com', + 'Path' => '/Space Out/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Discard' => null, + 'Secure' => true, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', + array( + 'Name' => 'domain', + 'Value' => 'unittests', + 'Domain' => 'example.com', + 'Path' => '/some value/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => false, + 'Discard' => null, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', + array( + 'Name' => 'path', + 'Value' => 'indexAction', + 'Domain' => '.foo.com', + 'Path' => '/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => false, + 'Discard' => null, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', + array( + 'Name' => 'secure', + 'Value' => 'sha1', + 'Domain' => 'some.really.deep.domain.com', + 'Path' => '/', + 'Secure' => true, + 'Discard' => null, + 'Expires' => time() + 86400, + 'Max-Age' => 86400, + 'HttpOnly' => false, + 'version' => '1' + ) + ), + array( + 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', + array( + 'Name' => 'PHPSESSID', + 'Value' => '123456789+abcd%2Cef', + 'Domain' => '.localdomain', + 'Path' => '/foo/baz', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => true, + 'Discard' => true, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + ); + } + + /** + * @dataProvider cookieParserDataProvider + */ + public function testParseCookie($cookie, $parsed) + { + foreach ((array) $cookie as $v) { + $c = SetCookie::fromString($v); + $p = $c->toArray(); + + if (isset($p['Expires'])) { + // Remove expires values from the assertion if they are relatively equal + if (abs($p['Expires'] != strtotime($parsed['Expires'])) < 40) { + unset($p['Expires']); + unset($parsed['Expires']); + } + } + + if (!empty($parsed)) { + foreach ($parsed as $key => $value) { + $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + foreach ($p as $key => $value) { + $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + } else { + $this->assertEquals([ + 'Name' => null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false, + ], $p); + } + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php new file mode 100644 index 00000000..b8c06f15 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php @@ -0,0 +1,14 @@ +getMockBuilder('GuzzleHttp\Event\AbstractEvent') + ->getMockForAbstractClass(); + $this->assertFalse($e->isPropagationStopped()); + $e->stopPropagation(); + $this->assertTrue($e->isPropagationStopped()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php new file mode 100644 index 00000000..50536c58 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php @@ -0,0 +1,33 @@ +getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertSame($t->client, $e->getClient()); + $this->assertSame($t->request, $e->getRequest()); + } + + public function testHasTransaction() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $r = new \ReflectionMethod($e, 'getTransaction'); + $r->setAccessible(true); + $this->assertSame($t, $r->invoke($e)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php new file mode 100644 index 00000000..6a39d8bb --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php @@ -0,0 +1,37 @@ +transferInfo = ['foo' => 'bar']; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRetryableEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $e->retry(); + $this->assertTrue($e->isPropagationStopped()); + $this->assertEquals('retry', $t->state); + } + + public function testCanRetryAfterDelay() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->transferInfo = ['foo' => 'bar']; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRetryableEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $e->retry(10); + $this->assertTrue($e->isPropagationStopped()); + $this->assertEquals('retry', $t->state); + $this->assertEquals(10, $t->request->getConfig()->get('delay')); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php new file mode 100644 index 00000000..5313c8e7 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php @@ -0,0 +1,59 @@ +transferInfo = ['foo' => 'bar']; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertNull($e->getTransferInfo('baz')); + $this->assertEquals('bar', $e->getTransferInfo('foo')); + $this->assertEquals($t->transferInfo, $e->getTransferInfo()); + } + + public function testHasResponse() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->response = new Response(200); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertTrue($e->hasResponse()); + $this->assertSame($t->response, $e->getResponse()); + } + + public function testCanInterceptWithResponse() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $r = new Response(200); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $e->intercept($r); + $this->assertSame($t->response, $r); + $this->assertSame($t->response, $e->getResponse()); + $this->assertTrue($e->isPropagationStopped()); + } + + public function testReturnsNumberOfRetries() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->retries = 2; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertEquals(2, $e->getRetryCount()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php new file mode 100644 index 00000000..469e4e25 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php @@ -0,0 +1,26 @@ +exception = new \Exception('foo'); + $e = new BeforeEvent($t); + $response = new Response(200); + $e->intercept($response); + $this->assertTrue($e->isPropagationStopped()); + $this->assertSame($t->response, $response); + $this->assertNull($t->exception); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php new file mode 100644 index 00000000..5b7061bc --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php @@ -0,0 +1,363 @@ +emitter = new Emitter(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->emitter = null; + $this->listener = null; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->emitter->listeners()); + } + + public function testAddListener() + { + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->emitter->hasListeners(self::preFoo)); + $this->assertTrue($this->emitter->hasListeners(self::preFoo)); + $this->assertCount(1, $this->emitter->listeners(self::postFoo)); + $this->assertCount(1, $this->emitter->listeners(self::postFoo)); + $this->assertCount(2, $this->emitter->listeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->emitter->on('pre.foo', array($listener1, 'preFoo'), -10); + $this->emitter->on('pre.foo', array($listener2, 'preFoo'), 10); + $this->emitter->on('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->emitter->listeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->emitter->on('pre.foo', [$listener1, 'preFoo'], -10); + $this->emitter->on('pre.foo', [$listener2, 'preFoo']); + $this->emitter->on('pre.foo', [$listener3, 'preFoo'], 10); + $this->emitter->on('post.foo', [$listener4, 'preFoo'], -10); + $this->emitter->on('post.foo', [$listener5, 'preFoo']); + $this->emitter->on('post.foo', [$listener6, 'preFoo'], 10); + + $expected = [ + 'pre.foo' => [[$listener3, 'preFoo'], [$listener2, 'preFoo'], [$listener1, 'preFoo']], + 'post.foo' => [[$listener6, 'preFoo'], [$listener5, 'preFoo'], [$listener4, 'preFoo']], + ]; + + $this->assertSame($expected, $this->emitter->listeners()); + } + + public function testDispatch() + { + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('post.foo', array($this->listener, 'postFoo')); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('GuzzleHttp\Event\EventInterface', $this->emitter->emit(self::preFoo, $this->getEvent())); + $event = $this->getEvent(); + $return = $this->emitter->emit(self::preFoo, $event); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->emitter->on('pre.foo', $listener); + $this->emitter->on('post.foo', $listener); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->emitter->on('post.foo', array($this->listener, 'postFoo'), 10); + $this->emitter->on('post.foo', array($otherListener, 'preFoo')); + $this->emitter->emit(self::postFoo, $this->getEvent()); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->emitter->on('pre.foo', $listener1, -10); + $this->emitter->on('pre.foo', $listener2); + $this->emitter->on('pre.foo', $listener3, 10); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->emitter->on('pre.bar', [$this->listener, 'preFoo']); + $this->assertNotEmpty($this->emitter->listeners(self::preBar)); + $this->emitter->removeListener('pre.bar', [$this->listener, 'preFoo']); + $this->assertEmpty($this->emitter->listeners(self::preBar)); + $this->emitter->removeListener('notExists', [$this->listener, 'preFoo']); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testAddSubscriberWithMultiple() + { + $eventSubscriber = new TestEventSubscriberWithMultiple(); + $this->emitter->attach($eventSubscriber); + $listeners = $this->emitter->listeners('pre.foo'); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertCount(2, $listeners); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->emitter->attach($eventSubscriber); + + $listeners = $this->emitter->listeners('pre.foo'); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('GuzzleHttp\Tests\Event\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testdetach() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + $this->emitter->detach($eventSubscriber); + $this->assertEmpty($this->emitter->listeners(self::preFoo)); + $this->assertEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testdetachWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + $this->emitter->detach($eventSubscriber); + $this->assertEmpty($this->emitter->listeners(self::preFoo)); + $this->assertEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testEventReceivesEventNameAsArgument() + { + $listener = new TestWithDispatcher(); + $this->emitter->on('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->emitter->emit('test', $this->getEvent()); + $this->assertEquals('test', $listener->name); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = new Emitter(); + $dispatcher->on('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertNotEmpty($dispatcher->listeners('bug.62976')); + } + + public function testRegistersEventsOnce() + { + $this->emitter->once('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->assertCount(2, $this->emitter->listeners(self::preFoo)); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertCount(1, $this->emitter->listeners(self::preFoo)); + } + + public function testReturnsEmptyArrayForNonExistentEvent() + { + $this->assertEquals([], $this->emitter->listeners('doesnotexist')); + } + + public function testCanAddFirstAndLastListeners() + { + $b = ''; + $this->emitter->on('foo', function () use (&$b) { $b .= 'a'; }, 'first'); // 1 + $this->emitter->on('foo', function () use (&$b) { $b .= 'b'; }, 'last'); // 0 + $this->emitter->on('foo', function () use (&$b) { $b .= 'c'; }, 'first'); // 2 + $this->emitter->on('foo', function () use (&$b) { $b .= 'd'; }, 'first'); // 3 + $this->emitter->on('foo', function () use (&$b) { $b .= 'e'; }, 'first'); // 4 + $this->emitter->on('foo', function () use (&$b) { $b .= 'f'; }); // 0 + $this->emitter->emit('foo', $this->getEvent()); + $this->assertEquals('edcabf', $b); + } + + /** + * @return \GuzzleHttp\Event\EventInterface + */ + private function getEvent() + { + return $this->getMockBuilder('GuzzleHttp\Event\AbstractEvent') + ->getMockForAbstractClass(); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(EventInterface $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(EventInterface $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + */ + public function testHasDeprecatedAddListener() + { + $emitter = new Emitter(); + $emitter->addListener('foo', function () {}); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + */ + public function testHasDeprecatedAddSubscriber() + { + $emitter = new Emitter(); + $emitter->addSubscriber('foo', new TestEventSubscriber()); + } +} + +class TestWithDispatcher +{ + public $name; + + public function foo(EventInterface $e, $name) + { + $this->name = $name; + } +} + +class TestEventSubscriber extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return [ + 'pre.foo' => ['preFoo'], + 'post.foo' => ['postFoo'] + ]; + } +} + +class TestEventSubscriberWithPriorities extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return [ + 'pre.foo' => ['preFoo', 10], + 'post.foo' => ['postFoo'] + ]; + } +} + +class TestEventSubscriberWithMultiple extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return ['pre.foo' => [['preFoo', 10],['preFoo', 20]]]; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php new file mode 100644 index 00000000..e91b7f0c --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php @@ -0,0 +1,23 @@ +request); + $t->exception = $except; + $e = new ErrorEvent($t); + $this->assertSame($e->getException(), $t->exception); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php new file mode 100644 index 00000000..47099187 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php @@ -0,0 +1,27 @@ +getMockBuilder('GuzzleHttp\Tests\Event\AbstractHasEmitter') + ->getMockForAbstractClass(); + + $result = $mock->getEmitter(); + $this->assertInstanceOf('GuzzleHttp\Event\EmitterInterface', $result); + $result2 = $mock->getEmitter(); + $this->assertSame($result, $result2); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php new file mode 100644 index 00000000..0b5d348f --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php @@ -0,0 +1,92 @@ +listeners = $this->prepareListeners($args, ['foo', 'bar']); + $this->attachListeners($this, $this->listeners); + } +} + +class ListenerAttacherTraitTest extends \PHPUnit_Framework_TestCase +{ + public function testRegistersEvents() + { + $fn = function () {}; + $o = new ObjectWithEvents([ + 'foo' => $fn, + 'bar' => $fn, + ]); + + $this->assertEquals([ + ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], + ], $o->listeners); + + $this->assertCount(1, $o->getEmitter()->listeners('foo')); + $this->assertCount(1, $o->getEmitter()->listeners('bar')); + } + + public function testRegistersEventsWithPriorities() + { + $fn = function () {}; + $o = new ObjectWithEvents([ + 'foo' => ['fn' => $fn, 'priority' => 99, 'once' => true], + 'bar' => ['fn' => $fn, 'priority' => 50], + ]); + + $this->assertEquals([ + ['name' => 'foo', 'fn' => $fn, 'priority' => 99, 'once' => true], + ['name' => 'bar', 'fn' => $fn, 'priority' => 50, 'once' => false], + ], $o->listeners); + } + + public function testRegistersMultipleEvents() + { + $fn = function () {}; + $eventArray = [['fn' => $fn], ['fn' => $fn]]; + $o = new ObjectWithEvents([ + 'foo' => $eventArray, + 'bar' => $eventArray, + ]); + + $this->assertEquals([ + ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], + ], $o->listeners); + + $this->assertCount(2, $o->getEmitter()->listeners('foo')); + $this->assertCount(2, $o->getEmitter()->listeners('bar')); + } + + public function testRegistersEventsWithOnce() + { + $called = 0; + $fn = function () use (&$called) { $called++; }; + $o = new ObjectWithEvents(['foo' => ['fn' => $fn, 'once' => true]]); + $ev = $this->getMock('GuzzleHttp\Event\EventInterface'); + $o->getEmitter()->emit('foo', $ev); + $o->getEmitter()->emit('foo', $ev); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEvents() + { + new ObjectWithEvents(['foo' => 'bar']); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php new file mode 100644 index 00000000..664f8b6b --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php @@ -0,0 +1,25 @@ +assertSame($t->request, $p->getRequest()); + $this->assertSame($t->client, $p->getClient()); + $this->assertEquals(2, $p->downloadSize); + $this->assertEquals(1, $p->downloaded); + $this->assertEquals(3, $p->uploadSize); + $this->assertEquals(0, $p->uploaded); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php new file mode 100644 index 00000000..b3b96660 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php @@ -0,0 +1,74 @@ + [$cb]]], + [ + ['complete' => $cb], + ['complete'], + $cb, + ['complete' => [$cb, $cb]] + ], + [ + ['prepare' => []], + ['error', 'foo'], + $cb, + [ + 'prepare' => [], + 'error' => [$cb], + 'foo' => [$cb] + ] + ], + [ + ['prepare' => []], + ['prepare'], + $cb, + [ + 'prepare' => [$cb] + ] + ], + [ + ['prepare' => ['fn' => $cb]], + ['prepare'], $cb, + [ + 'prepare' => [ + ['fn' => $cb], + $cb + ] + ] + ], + ]; + } + + /** + * @dataProvider prepareEventProvider + */ + public function testConvertsEventArrays( + array $in, + array $events, + $add, + array $out + ) { + $result = RequestEvents::convertEventArray($in, $events, $add); + $this->assertEquals($out, $result); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventFormat() + { + RequestEvents::convertEventArray(['foo' => false], ['foo'], []); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php new file mode 100644 index 00000000..4ff9bfb6 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php @@ -0,0 +1,20 @@ +assertSame($res, $e->getResponse()); + $this->assertEquals('foo', $e->getMessage()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php new file mode 100644 index 00000000..bea9077b --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php @@ -0,0 +1,83 @@ +assertSame($req, $e->getRequest()); + $this->assertSame($res, $e->getResponse()); + $this->assertTrue($e->hasResponse()); + $this->assertEquals('foo', $e->getMessage()); + } + + public function testCreatesGenerateException() + { + $e = RequestException::create(new Request('GET', '/')); + $this->assertEquals('Error completing request', $e->getMessage()); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function testCreatesClientErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(400)); + $this->assertEquals( + 'Client error response [url] / [status code] 400 [reason phrase] Bad Request', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $e); + } + + public function testCreatesServerErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(500)); + $this->assertEquals( + 'Server error response [url] / [status code] 500 [reason phrase] Internal Server Error', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\ServerException', $e); + } + + public function testCreatesGenericErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(600)); + $this->assertEquals( + 'Unsuccessful response [url] / [status code] 600 [reason phrase] ', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function testHasStatusCodeAsExceptionCode() { + $e = RequestException::create(new Request('GET', '/'), new Response(442)); + $this->assertEquals(442, $e->getCode()); + } + + public function testWrapsRequestExceptions() + { + $e = new \Exception('foo'); + $r = new Request('GET', 'http://www.oo.com'); + $ex = RequestException::wrapException($r, $e); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $ex); + $this->assertSame($e, $ex->getPrevious()); + } + + public function testWrapsConnectExceptions() + { + $e = new ConnectException('foo'); + $r = new Request('GET', 'http://www.oo.com'); + $ex = RequestException::wrapException($r, $e); + $this->assertInstanceOf('GuzzleHttp\Exception\ConnectException', $ex); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php new file mode 100644 index 00000000..51b97425 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php @@ -0,0 +1,19 @@ +assertSame($error, $e->getError()); + $this->assertEquals('foo', $e->getMessage()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php new file mode 100644 index 00000000..e26c64d9 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php @@ -0,0 +1,123 @@ +createRequest( + 'GET', + Server::$url, + [ + 'timeout' => 1, + 'connect_timeout' => 1, + 'proxy' => 'http://127.0.0.1:123/foo' + ] + ); + + $events = []; + $fn = function(AbstractTransferEvent $event) use (&$events) { + $events[] = [ + get_class($event), + $event->hasResponse(), + $event->getResponse() + ]; + }; + + $pool = new Pool($c, [$r], [ + 'error' => $fn, + 'end' => $fn + ]); + + $pool->wait(); + + $this->assertCount(2, $events); + $this->assertEquals('GuzzleHttp\Event\ErrorEvent', $events[0][0]); + $this->assertFalse($events[0][1]); + $this->assertNull($events[0][2]); + + $this->assertEquals('GuzzleHttp\Event\EndEvent', $events[1][0]); + $this->assertFalse($events[1][1]); + $this->assertNull($events[1][2]); + } + + /** + * @issue https://github.com/guzzle/guzzle/issues/866 + */ + public function testProperyGetsTransferStats() + { + $transfer = []; + Server::enqueue([new Response(200)]); + $c = new Client(); + $response = $c->get(Server::$url . '/foo', [ + 'events' => [ + 'end' => function (EndEvent $e) use (&$transfer) { + $transfer = $e->getTransferInfo(); + } + ] + ]); + $this->assertEquals(Server::$url . '/foo', $response->getEffectiveUrl()); + $this->assertNotEmpty($transfer); + $this->assertArrayHasKey('url', $transfer); + } + + public function testNestedFutureResponsesAreResolvedWhenSending() + { + $c = new Client(); + $total = 3; + Server::enqueue([ + new Response(200), + new Response(201), + new Response(202) + ]); + $c->getEmitter()->on( + 'complete', + function (CompleteEvent $e) use (&$total) { + if (--$total) { + $e->retry(); + } + } + ); + $response = $c->get(Server::$url); + $this->assertEquals(202, $response->getStatusCode()); + $this->assertEquals('GuzzleHttp\Message\Response', get_class($response)); + } + + public function testNestedFutureErrorsAreResolvedWhenSending() + { + $c = new Client(); + $total = 3; + Server::enqueue([ + new Response(500), + new Response(501), + new Response(502) + ]); + $c->getEmitter()->on( + 'error', + function (ErrorEvent $e) use (&$total) { + if (--$total) { + $e->retry(); + } + } + ); + try { + $c->get(Server::$url); + $this->fail('Did not throw!'); + } catch (RequestException $e) { + $this->assertEquals(502, $e->getResponse()->getStatusCode()); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php new file mode 100644 index 00000000..f02a576f --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php @@ -0,0 +1,269 @@ +assertEquals(1.1, $m->getProtocolVersion()); + } + + public function testHasHeaders() + { + $m = new Request('GET', 'http://foo.com'); + $this->assertFalse($m->hasHeader('foo')); + $m->addHeader('foo', 'bar'); + $this->assertTrue($m->hasHeader('foo')); + } + + public function testInitializesMessageWithProtocolVersionOption() + { + $m = new Request('GET', '/', [], null, [ + 'protocol_version' => '10' + ]); + $this->assertEquals(10, $m->getProtocolVersion()); + } + + public function testHasBody() + { + $m = new Request('GET', 'http://foo.com'); + $this->assertNull($m->getBody()); + $s = Stream::factory('test'); + $m->setBody($s); + $this->assertSame($s, $m->getBody()); + $this->assertFalse($m->hasHeader('Content-Length')); + } + + public function testCanRemoveBodyBySettingToNullAndRemovesCommonBodyHeaders() + { + $m = new Request('GET', 'http://foo.com'); + $m->setBody(Stream::factory('foo')); + $m->setHeader('Content-Length', 3); + $m->setHeader('Transfer-Encoding', 'chunked'); + $m->setBody(null); + $this->assertNull($m->getBody()); + $this->assertFalse($m->hasHeader('Content-Length')); + $this->assertFalse($m->hasHeader('Transfer-Encoding')); + } + + public function testCastsToString() + { + $m = new Request('GET', 'http://foo.com'); + $m->setHeader('foo', 'bar'); + $m->setBody(Stream::factory('baz')); + $this->assertEquals("GET / HTTP/1.1\r\nHost: foo.com\r\nfoo: bar\r\n\r\nbaz", (string) $m); + } + + public function parseParamsProvider() + { + $res1 = array( + array( + '', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + array( + '', + 'rel' => 'back', + 'type' => 'image/jpeg', + ), + ); + + return array( + array( + '; rel="front"; type="image/jpeg", ; rel=back; type="image/jpeg"', + $res1 + ), + array( + '; rel="front"; type="image/jpeg",; rel=back; type="image/jpeg"', + $res1 + ), + array( + 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', + array( + array('foo' => 'baz', 'bar' => '123'), + array('boo'), + array('test' => '123'), + array('foobar' => 'foo;bar') + ) + ), + array( + '; rel="side"; type="image/jpeg",; rel=side; type="image/jpeg"', + array( + array('', 'rel' => 'side', 'type' => 'image/jpeg'), + array('', 'rel' => 'side', 'type' => 'image/jpeg') + ) + ), + array( + '', + array() + ) + ); + } + + /** + * @dataProvider parseParamsProvider + */ + public function testParseParams($header, $result) + { + $request = new Request('GET', '/', ['foo' => $header]); + $this->assertEquals($result, Request::parseHeader($request, 'foo')); + } + + public function testAddsHeadersWhenNotPresent() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('foo', 'bar'); + $this->assertInternalType('string', $h->getHeader('foo')); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testAddsHeadersWhenPresentSameCase() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('foo', 'bar'); + $h->addHeader('foo', 'baz'); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + $this->assertEquals(['bar', 'baz'], $h->getHeaderAsArray('foo')); + } + + public function testAddsMultipleHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeaders([ + 'foo' => ' bar', + 'baz' => [' bam ', 'boo'] + ]); + $this->assertEquals([ + 'foo' => ['bar'], + 'baz' => ['bam', 'boo'], + 'Host' => ['foo.com'] + ], $h->getHeaders()); + } + + public function testAddsHeadersWhenPresentDifferentCase() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('Foo', 'bar'); + $h->addHeader('fOO', 'baz'); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + } + + public function testAddsHeadersWithArray() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('Foo', ['bar', 'baz']); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + } + + public function testGetHeadersReturnsAnArrayOfOverTheWireHeaderValues() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('foo', 'bar'); + $h->addHeader('Foo', 'baz'); + $h->addHeader('boO', 'test'); + $result = $h->getHeaders(); + $this->assertInternalType('array', $result); + $this->assertArrayHasKey('Foo', $result); + $this->assertArrayNotHasKey('foo', $result); + $this->assertArrayHasKey('boO', $result); + $this->assertEquals(['bar', 'baz'], $result['Foo']); + $this->assertEquals(['test'], $result['boO']); + } + + public function testSetHeaderOverwritesExistingValues() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $this->assertEquals('bar', $h->getHeader('foo')); + $h->setHeader('Foo', 'baz'); + $this->assertEquals('baz', $h->getHeader('foo')); + $this->assertArrayHasKey('Foo', $h->getHeaders()); + } + + public function testSetHeaderOverwritesExistingValuesUsingHeaderArray() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', ['bar']); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testSetHeaderOverwritesExistingValuesUsingArray() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', ['bar']); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testSetHeadersOverwritesAllHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $h->setHeaders(['foo' => 'a', 'boo' => 'b']); + $this->assertEquals(['foo' => ['a'], 'boo' => ['b']], $h->getHeaders()); + } + + public function testChecksIfCaseInsensitiveHeaderIsPresent() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $this->assertTrue($h->hasHeader('foo')); + $this->assertTrue($h->hasHeader('Foo')); + $h->setHeader('fOo', 'bar'); + $this->assertTrue($h->hasHeader('Foo')); + } + + public function testRemovesHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $h->removeHeader('foo'); + $this->assertFalse($h->hasHeader('foo')); + $h->setHeader('Foo', 'bar'); + $h->removeHeader('FOO'); + $this->assertFalse($h->hasHeader('foo')); + } + + public function testReturnsCorrectTypeWhenMissing() + { + $h = new Request('GET', 'http://foo.com'); + $this->assertInternalType('string', $h->getHeader('foo')); + $this->assertInternalType('array', $h->getHeaderAsArray('foo')); + } + + public function testSetsIntegersAndFloatsAsHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 10); + $h->setHeader('bar', 10.5); + $h->addHeader('foo', 10); + $h->addHeader('bar', 10.5); + $this->assertSame('10, 10', $h->getHeader('foo')); + $this->assertSame('10.5, 10.5', $h->getHeader('bar')); + } + + public function testGetsResponseStartLine() + { + $m = new Response(200); + $this->assertEquals('HTTP/1.1 200 OK', Response::getStartLine($m)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsWhenMessageIsUnknown() + { + $m = $this->getMockBuilder('GuzzleHttp\Message\AbstractMessage') + ->getMockForAbstractClass(); + AbstractMessage::getStartLine($m); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php new file mode 100644 index 00000000..771631d5 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php @@ -0,0 +1,160 @@ +foo; + } + + public function testDoesTheSameAsResponseWhenDereferenced() + { + $str = Stream::factory('foo'); + $response = new Response(200, ['Foo' => 'bar'], $str); + $future = MockTest::createFuture(function () use ($response) { + return $response; + }); + $this->assertFalse($this->readAttribute($future, 'isRealized')); + $this->assertEquals(200, $future->getStatusCode()); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + // Deref again does nothing. + $future->wait(); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + $this->assertEquals('bar', $future->getHeader('Foo')); + $this->assertEquals(['bar'], $future->getHeaderAsarray('Foo')); + $this->assertSame($response->getHeaders(), $future->getHeaders()); + $this->assertSame( + $response->getBody(), + $future->getBody() + ); + $this->assertSame( + $response->getProtocolVersion(), + $future->getProtocolVersion() + ); + $this->assertSame( + $response->getEffectiveUrl(), + $future->getEffectiveUrl() + ); + $future->setEffectiveUrl('foo'); + $this->assertEquals('foo', $response->getEffectiveUrl()); + $this->assertSame( + $response->getReasonPhrase(), + $future->getReasonPhrase() + ); + + $this->assertTrue($future->hasHeader('foo')); + + $future->removeHeader('Foo'); + $this->assertFalse($future->hasHeader('foo')); + $this->assertFalse($response->hasHeader('foo')); + + $future->setBody(Stream::factory('true')); + $this->assertEquals('true', (string) $response->getBody()); + $this->assertTrue($future->json()); + $this->assertSame((string) $response, (string) $future); + + $future->setBody(Stream::factory('c')); + $this->assertEquals('c', (string) $future->xml()->b); + + $future->addHeader('a', 'b'); + $this->assertEquals('b', $future->getHeader('a')); + + $future->addHeaders(['a' => '2']); + $this->assertEquals('b, 2', $future->getHeader('a')); + + $future->setHeader('a', '2'); + $this->assertEquals('2', $future->getHeader('a')); + + $future->setHeaders(['a' => '3']); + $this->assertEquals(['a' => ['3']], $future->getHeaders()); + } + + public function testCanDereferenceManually() + { + $response = new Response(200, ['Foo' => 'bar']); + $future = MockTest::createFuture(function () use ($response) { + return $response; + }); + $this->assertSame($response, $future->wait()); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + } + + public function testCanCancel() + { + $c = false; + $deferred = new Deferred(); + $future = new FutureResponse( + $deferred->promise(), + function () {}, + function () use (&$c) { + $c = true; + return true; + } + ); + + $this->assertFalse($this->readAttribute($future, 'isRealized')); + $future->cancel(); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + $future->cancel(); + } + + public function testCanCancelButReturnsFalseForNoCancelFunction() + { + $future = MockTest::createFuture(function () {}); + $future->cancel(); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + } + + /** + * @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException + */ + public function testAccessingCancelledResponseThrows() + { + $future = MockTest::createFuture(function () {}); + $future->cancel(); + $future->getStatusCode(); + } + + public function testExceptionInToStringTriggersError() + { + $future = MockTest::createFuture(function () { + throw new \Exception('foo'); + }); + $err = ''; + set_error_handler(function () use (&$err) { + $err = func_get_args()[1]; + }); + echo $future; + restore_error_handler(); + $this->assertContains('foo', $err); + } + + public function testProxiesSetters() + { + $str = Stream::factory('foo'); + $response = new Response(200, ['Foo' => 'bar'], $str); + $future = MockTest::createFuture(function () use ($response) { + return $response; + }); + + $future->setStatusCode(202); + $this->assertEquals(202, $future->getStatusCode()); + $this->assertEquals(202, $response->getStatusCode()); + + $future->setReasonPhrase('foo'); + $this->assertEquals('foo', $future->getReasonPhrase()); + $this->assertEquals('foo', $response->getReasonPhrase()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php new file mode 100644 index 00000000..45082487 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php @@ -0,0 +1,607 @@ +createResponse(200, ['foo' => 'bar'], 'test', [ + 'protocol_version' => 1.0 + ]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(['foo' => ['bar']], $response->getHeaders()); + $this->assertEquals('test', $response->getBody()); + $this->assertEquals(1.0, $response->getProtocolVersion()); + } + + public function testCreatesRequestFromMessage() + { + $f = new MessageFactory(); + $req = $f->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\n"); + $this->assertEquals('GET', $req->getMethod()); + $this->assertEquals('/', $req->getPath()); + $this->assertEquals('foo', $req->getHeader('Baz')); + $this->assertNull($req->getBody()); + } + + public function testCreatesRequestFromMessageWithBody() + { + $req = (new MessageFactory())->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\ntest"); + $this->assertEquals('test', $req->getBody()); + } + + public function testCreatesRequestWithPostBody() + { + $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', ['body' => ['abc' => '123']]); + $this->assertEquals('abc=123', $req->getBody()); + } + + public function testCreatesRequestWithPostBodyScalars() + { + $req = (new MessageFactory())->createRequest( + 'GET', + 'http://www.foo.com', + ['body' => [ + 'abc' => true, + '123' => false, + 'foo' => null, + 'baz' => 10, + 'bam' => 1.5, + 'boo' => [1]] + ] + ); + $this->assertEquals( + 'abc=1&123=&foo&baz=10&bam=1.5&boo%5B0%5D=1', + (string) $req->getBody() + ); + } + + public function testCreatesRequestWithPostBodyAndPostFiles() + { + $pf = fopen(__FILE__, 'r'); + $pfi = new PostFile('ghi', 'abc', __FILE__); + $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', [ + 'body' => [ + 'abc' => '123', + 'def' => $pf, + 'ghi' => $pfi + ] + ]); + $this->assertInstanceOf('GuzzleHttp\Post\PostBody', $req->getBody()); + $s = (string) $req; + $this->assertContains('testCreatesRequestWithPostBodyAndPostFiles', $s); + $this->assertContains('multipart/form-data', $s); + $this->assertTrue(in_array($pfi, $req->getBody()->getFiles(), true)); + } + + public function testCreatesResponseFromMessage() + { + $response = (new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals('4', $response->getHeader('Content-Length')); + $this->assertEquals('test', $response->getBody(true)); + } + + public function testCanCreateHeadResponses() + { + $response = (new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(null, $response->getBody()); + $this->assertEquals('4', $response->getHeader('Content-Length')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFactoryRequiresMessageForRequest() + { + (new MessageFactory())->fromMessage(''); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage foo + */ + public function testValidatesOptionsAreImplemented() + { + (new MessageFactory())->createRequest('GET', 'http://test.com', ['foo' => 'bar']); + } + + public function testOptionsAddsRequestOptions() + { + $request = (new MessageFactory())->createRequest( + 'GET', 'http://test.com', ['config' => ['baz' => 'bar']] + ); + $this->assertEquals('bar', $request->getConfig()->get('baz')); + } + + public function testCanDisableRedirects() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => false]); + $this->assertEmpty($request->getEmitter()->listeners('complete')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesRedirects() + { + (new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => 'foo']); + } + + public function testCanEnableStrictRedirectsAndSpecifyMax() + { + $request = (new MessageFactory())->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + $this->assertTrue($request->getConfig()['redirect']['strict']); + $this->assertEquals(10, $request->getConfig()['redirect']['max']); + } + + public function testCanAddCookiesFromHash() + { + $request = (new MessageFactory())->createRequest('GET', 'http://www.test.com/', [ + 'cookies' => ['Foo' => 'Bar'] + ]); + $cookies = null; + foreach ($request->getEmitter()->listeners('before') as $l) { + if ($l[0] instanceof Cookie) { + $cookies = $l[0]; + break; + } + } + if (!$cookies) { + $this->fail('Did not add cookie listener'); + } else { + $this->assertCount(1, $cookies->getCookieJar()); + } + } + + public function testAddsCookieUsingTrue() + { + $factory = new MessageFactory(); + $request1 = $factory->createRequest('GET', '/', ['cookies' => true]); + $request2 = $factory->createRequest('GET', '/', ['cookies' => true]); + $listeners = function ($r) { + return array_filter($r->getEmitter()->listeners('before'), function ($l) { + return $l[0] instanceof Cookie; + }); + }; + $this->assertSame($listeners($request1), $listeners($request2)); + } + + public function testAddsCookieFromCookieJar() + { + $jar = new CookieJar(); + $request = (new MessageFactory())->createRequest('GET', '/', ['cookies' => $jar]); + foreach ($request->getEmitter()->listeners('before') as $l) { + if ($l[0] instanceof Cookie) { + $this->assertSame($jar, $l[0]->getCookieJar()); + } + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCookies() + { + (new MessageFactory())->createRequest('GET', '/', ['cookies' => 'baz']); + } + + public function testCanAddQuery() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'query' => ['Foo' => 'Bar'] + ]); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesQuery() + { + (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'query' => 'foo' + ]); + } + + public function testCanSetDefaultQuery() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com?test=abc', [ + 'query' => ['Foo' => 'Bar', 'test' => 'def'] + ]); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + $this->assertEquals('abc', $request->getQuery()->get('test')); + } + + public function testCanSetDefaultQueryWithObject() + { + $request = (new MessageFactory)->createRequest( + 'GET', + 'http://foo.com?test=abc', [ + 'query' => new Query(['Foo' => 'Bar', 'test' => 'def']) + ] + ); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + $this->assertEquals('abc', $request->getQuery()->get('test')); + } + + public function testCanAddBasicAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => ['michael', 'test'] + ]); + $this->assertTrue($request->hasHeader('Authorization')); + } + + public function testCanAddDigestAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => ['michael', 'test', 'digest'] + ]); + $this->assertEquals('michael:test', $request->getConfig()->getPath('curl/' . CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getConfig()->getPath('curl/' . CURLOPT_HTTPAUTH)); + } + + public function testCanDisableAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => false + ]); + $this->assertFalse($request->hasHeader('Authorization')); + } + + public function testCanSetCustomAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => 'foo' + ]); + $this->assertEquals('foo', $request->getConfig()['auth']); + } + + public function testCanAddEvents() + { + $foo = null; + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $client->get('http://test.com', [ + 'events' => [ + 'before' => function () use (&$foo) { $foo = true; } + ] + ]); + $this->assertTrue($foo); + } + + public function testCanAddEventsWithPriority() + { + $foo = null; + $client = new Client(); + $client->getEmitter()->attach(new Mock(array(new Response(200)))); + $request = $client->createRequest('GET', 'http://test.com', [ + 'events' => [ + 'before' => [ + 'fn' => function () use (&$foo) { $foo = true; }, + 'priority' => 123 + ] + ] + ]); + $client->send($request); + $this->assertTrue($foo); + $l = $this->readAttribute($request->getEmitter(), 'listeners'); + $this->assertArrayHasKey(123, $l['before']); + } + + public function testCanAddEventsOnce() + { + $foo = 0; + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(200), + new Response(200), + ])); + $fn = function () use (&$foo) { ++$foo; }; + $request = $client->createRequest('GET', 'http://test.com', [ + 'events' => ['before' => ['fn' => $fn, 'once' => true]] + ]); + $client->send($request); + $this->assertEquals(1, $foo); + $client->send($request); + $this->assertEquals(1, $foo); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventContainsFn() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->createRequest('GET', '/', ['events' => ['before' => ['foo' => 'bar']]]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventIsArray() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->createRequest('GET', '/', ['events' => ['before' => '123']]); + } + + public function testCanAddSubscribers() + { + $mock = new Mock([new Response(200)]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->get('http://test.com', ['subscribers' => [$mock]]); + } + + public function testCanDisableExceptions() + { + $client = new Client(); + $this->assertEquals(500, $client->get('http://test.com', [ + 'subscribers' => [new Mock([new Response(500)])], + 'exceptions' => false + ])->getStatusCode()); + } + + public function testCanChangeSaveToLocation() + { + $saveTo = Stream::factory(); + $request = (new MessageFactory())->createRequest('GET', '/', ['save_to' => $saveTo]); + $this->assertSame($saveTo, $request->getConfig()->get('save_to')); + } + + public function testCanSetProxy() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['proxy' => '192.168.16.121']); + $this->assertEquals('192.168.16.121', $request->getConfig()->get('proxy')); + } + + public function testCanSetHeadersOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['headers' => ['Foo' => 'Bar']]); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + } + + public function testCanSetHeaders() + { + $request = (new MessageFactory())->createRequest('GET', '/', [ + 'headers' => ['Foo' => ['Baz', 'Bar'], 'Test' => '123'] + ]); + $this->assertEquals('Baz, Bar', $request->getHeader('Foo')); + $this->assertEquals('123', $request->getHeader('Test')); + } + + public function testCanSetTimeoutOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['timeout' => 1.5]); + $this->assertEquals(1.5, $request->getConfig()->get('timeout')); + } + + public function testCanSetConnectTimeoutOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['connect_timeout' => 1.5]); + $this->assertEquals(1.5, $request->getConfig()->get('connect_timeout')); + } + + public function testCanSetDebug() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['debug' => true]); + $this->assertTrue($request->getConfig()->get('debug')); + } + + public function testCanSetVerifyToOff() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => false]); + $this->assertFalse($request->getConfig()->get('verify')); + } + + public function testCanSetVerifyToOn() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => true]); + $this->assertTrue($request->getConfig()->get('verify')); + } + + public function testCanSetVerifyToPath() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('verify')); + } + + public function inputValidation() + { + return array_map(function ($option) { return array($option); }, array( + 'headers', 'events', 'subscribers', 'params' + )); + } + + /** + * @dataProvider inputValidation + * @expectedException \InvalidArgumentException + */ + public function testValidatesInput($option) + { + (new MessageFactory())->createRequest('GET', '/', [$option => 'foo']); + } + + public function testCanAddSslKey() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('ssl_key')); + } + + public function testCanAddSslKeyPassword() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => ['/foo.pem', 'bar']]); + $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('ssl_key')); + } + + public function testCanAddSslCert() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('cert')); + } + + public function testCanAddSslCertPassword() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => ['/foo.pem', 'bar']]); + $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('cert')); + } + + public function testCreatesBodyWithoutZeroString() + { + $request = (new MessageFactory())->createRequest('PUT', 'http://test.com', ['body' => '0']); + $this->assertSame('0', (string) $request->getBody()); + } + + public function testCanSetProtocolVersion() + { + $request = (new MessageFactory())->createRequest('GET', 'http://t.com', ['version' => 1.0]); + $this->assertEquals(1.0, $request->getProtocolVersion()); + } + + public function testCanAddJsonData() + { + $request = (new MessageFactory())->createRequest('PUT', 'http://f.com', [ + 'json' => ['foo' => 'bar'] + ]); + $this->assertEquals( + 'application/json', + $request->getHeader('Content-Type') + ); + $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); + } + + public function testCanAddJsonDataToAPostRequest() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.com', [ + 'json' => ['foo' => 'bar'] + ]); + $this->assertEquals( + 'application/json', + $request->getHeader('Content-Type') + ); + $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); + } + + public function testCanAddJsonDataAndNotOverwriteContentType() + { + $request = (new MessageFactory())->createRequest('PUT', 'http://f.com', [ + 'headers' => ['Content-Type' => 'foo'], + 'json' => null + ]); + $this->assertEquals('foo', $request->getHeader('Content-Type')); + $this->assertEquals('null', (string) $request->getBody()); + } + + public function testCanUseCustomRequestOptions() + { + $c = false; + $f = new MessageFactory([ + 'foo' => function (RequestInterface $request, $value) use (&$c) { + $c = true; + $this->assertEquals('bar', $value); + } + ]); + + $f->createRequest('PUT', 'http://f.com', [ + 'headers' => ['Content-Type' => 'foo'], + 'foo' => 'bar' + ]); + + $this->assertTrue($c); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/706 + */ + public function testDoesNotApplyPostBodyRightAway() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'body' => ['foo' => ['bar', 'baz']] + ]); + $this->assertEquals('', $request->getHeader('Content-Type')); + $this->assertEquals('', $request->getHeader('Content-Length')); + $request->getBody()->setAggregator(Query::duplicateAggregator()); + $request->getBody()->applyRequestHeaders($request); + $this->assertEquals('foo=bar&foo=baz', $request->getBody()); + } + + public function testDoesNotRequireUppercasePost() + { + $request = (new MessageFactory())->createRequest('post', 'http://foo.com'); + $request->getBody()->setField('foo', 'bar'); + } + + public function testCanForceMultipartUploadWithContentType() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $history = new History(); + $client->getEmitter()->attach($history); + $client->post('http://foo.com', [ + 'headers' => ['Content-Type' => 'multipart/form-data'], + 'body' => ['foo' => 'bar'] + ]); + $this->assertContains( + 'multipart/form-data; boundary=', + $history->getLastRequest()->getHeader('Content-Type') + ); + $this->assertContains( + "Content-Disposition: form-data; name=\"foo\"\r\n\r\nbar", + (string) $history->getLastRequest()->getBody() + ); + } + + public function testDecodeDoesNotForceAcceptHeader() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'decode_content' => true + ]); + $this->assertEquals('', $request->getHeader('Accept-Encoding')); + $this->assertTrue($request->getConfig()->get('decode_content')); + } + + public function testDecodeCanAddAcceptHeader() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'decode_content' => 'gzip' + ]); + $this->assertEquals('gzip', $request->getHeader('Accept-Encoding')); + $this->assertTrue($request->getConfig()->get('decode_content')); + } + + public function testCanDisableDecoding() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'decode_content' => false + ]); + $this->assertEquals('', $request->getHeader('Accept-Encoding')); + $this->assertNull($request->getConfig()->get('decode_content')); + } +} + +class ExtendedFactory extends MessageFactory +{ + protected function add_foo() {} +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php new file mode 100644 index 00000000..0bcc9430 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php @@ -0,0 +1,276 @@ +compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new MessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } + + public function testParsesRequestsWithMissingProtocol() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function testParsesRequestsWithMissingVersion() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function testParsesResponsesWithMissingReasonPhrase() + { + $parser = new MessageParser(); + $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); + $this->assertEquals('200', $parts['code']); + $this->assertEquals('', $parts['reason_phrase']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function requestProvider() + { + $auth = base64_encode('michael:foo'); + + return array( + + // Empty request + array('', false), + + // Converts casing of request. Does not require host header. + array("GET / HTTP/1.1\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => '', + 'port' => '', + 'path' => '/', + 'query' => '' + ), + 'headers' => array(), + 'body' => '' + )), + // Path and query string, multiple header values per header and case sensitive storage + array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( + 'method' => 'HEAD', + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '', + 'path' => '/path', + 'query' => 'query=foo' + ), + 'headers' => array( + 'Host' => 'example.com', + 'X-Foo' => array('foo', 'foo', 'Baz'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + // Includes a body + array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( + 'method' => 'PUT', + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'request_url' => array( + 'scheme' => 'https', + 'host' => 'example.com', + 'port' => '443', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'host' => 'example.com:443', + 'Content-Length' => '4' + ), + 'body' => 'test' + )), + // Includes Authorization headers + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'Authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + // Include authorization header + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + ); + } + + public function responseProvider() + { + return array( + // Empty request + array('', false), + + array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'OK', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'code' => '400', + 'reason_phrase' => 'Bad Request', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 100 Continue\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'code' => '100', + 'reason_phrase' => 'Continue', + 'headers' => array(), + 'body' => '' + )), + array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '204', + 'reason_phrase' => 'No Content', + 'headers' => array( + 'X-Foo' => array('foo', 'foo'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'Ok that is great!', + 'headers' => array( + 'Content-Length' => 4 + ), + 'body' => 'Test' + )), + ); + } + + public function compareRequestResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['method'], $expected['method']); + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['protocol_version'], $expected['protocol_version']); + $this->assertEquals($result['request_url'], $expected['request_url']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + public function compareResponseResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['protocol_version'], $expected['protocol_version']); + $this->assertEquals($result['code'], $expected['code']); + $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + protected function normalizeHeaders($headers) + { + $normalized = array(); + foreach ($headers as $key => $value) { + $key = strtolower($key); + if (!isset($normalized[$key])) { + $normalized[$key] = $value; + } elseif (!is_array($normalized[$key])) { + $normalized[$key] = array($value); + } else { + $normalized[$key][] = $value; + } + } + + foreach ($normalized as $key => &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $normalized; + } + + public function compareHttpHeaders($result, $expected) + { + // Aggregate all headers case-insensitively + $result = $this->normalizeHeaders($result); + $expected = $this->normalizeHeaders($expected); + $this->assertEquals($result, $expected); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php new file mode 100644 index 00000000..4e670a49 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php @@ -0,0 +1,144 @@ + '123'], Stream::factory('foo')); + $this->assertEquals('PUT', $r->getMethod()); + $this->assertEquals('/test', $r->getUrl()); + $this->assertEquals('123', $r->getHeader('test')); + $this->assertEquals('foo', $r->getBody()); + } + + public function testConstructorInitializesMessageWithMixedCaseHeaders() + { + $r = new Request('GET', '/test', [ + 'Set-Cookie' => 'foo=bar, baz=bam', + 'Set-cookie' => 'hi=there', + 'other' => ['1', '2'] + ]); + + $this->assertEquals('foo=bar, baz=bam, hi=there', $r->getHeader('Set-Cookie')); + $this->assertEquals('1, 2', $r->getHeader('other')); + } + + public function testConstructorInitializesMessageWithProtocolVersion() + { + $r = new Request('GET', '', [], null, ['protocol_version' => 10]); + $this->assertEquals(10, $r->getProtocolVersion()); + } + + public function testConstructorInitializesMessageWithEmitter() + { + $e = new Emitter(); + $r = new Request('GET', '', [], null, ['emitter' => $e]); + $this->assertSame($r->getEmitter(), $e); + } + + public function testCloneIsDeep() + { + $r = new Request('GET', '/test', ['foo' => 'baz'], Stream::factory('foo')); + $r2 = clone $r; + + $this->assertNotSame($r->getEmitter(), $r2->getEmitter()); + $this->assertEquals('foo', $r2->getBody()); + + $r->getConfig()->set('test', 123); + $this->assertFalse($r2->getConfig()->hasKey('test')); + + $r->setPath('/abc'); + $this->assertEquals('/test', $r2->getPath()); + } + + public function testCastsToString() + { + $r = new Request('GET', 'http://test.com/test', ['foo' => 'baz'], Stream::factory('body')); + $s = explode("\r\n", (string) $r); + $this->assertEquals("GET /test HTTP/1.1", $s[0]); + $this->assertContains('Host: test.com', $s); + $this->assertContains('foo: baz', $s); + $this->assertContains('', $s); + $this->assertContains('body', $s); + } + + public function testSettingUrlOverridesHostHeaders() + { + $r = new Request('GET', 'http://test.com/test'); + $r->setUrl('https://baz.com/bar'); + $this->assertEquals('baz.com', $r->getHost()); + $this->assertEquals('baz.com', $r->getHeader('Host')); + $this->assertEquals('/bar', $r->getPath()); + $this->assertEquals('https', $r->getScheme()); + } + + public function testQueryIsMutable() + { + $r = new Request('GET', 'http://www.foo.com?baz=bar'); + $this->assertEquals('baz=bar', $r->getQuery()); + $this->assertInstanceOf('GuzzleHttp\Query', $r->getQuery()); + $r->getQuery()->set('hi', 'there'); + $this->assertEquals('/?baz=bar&hi=there', $r->getResource()); + } + + public function testQueryCanChange() + { + $r = new Request('GET', 'http://www.foo.com?baz=bar'); + $r->setQuery(new Query(['foo' => 'bar'])); + $this->assertEquals('foo=bar', $r->getQuery()); + } + + public function testCanChangeMethod() + { + $r = new Request('GET', 'http://www.foo.com'); + $r->setMethod('put'); + $this->assertEquals('PUT', $r->getMethod()); + } + + public function testCanChangeSchemeWithPort() + { + $r = new Request('GET', 'http://www.foo.com:80'); + $r->setScheme('https'); + $this->assertEquals('https://www.foo.com', $r->getUrl()); + } + + public function testCanChangeScheme() + { + $r = new Request('GET', 'http://www.foo.com'); + $r->setScheme('https'); + $this->assertEquals('https://www.foo.com', $r->getUrl()); + } + + public function testCanChangeHost() + { + $r = new Request('GET', 'http://www.foo.com:222'); + $r->setHost('goo'); + $this->assertEquals('http://goo:222', $r->getUrl()); + $this->assertEquals('goo:222', $r->getHeader('host')); + $r->setHost('goo:80'); + $this->assertEquals('http://goo', $r->getUrl()); + $this->assertEquals('goo', $r->getHeader('host')); + } + + public function testCanChangePort() + { + $r = new Request('GET', 'http://www.foo.com:222'); + $this->assertSame(222, $r->getPort()); + $this->assertEquals('www.foo.com', $r->getHost()); + $this->assertEquals('www.foo.com:222', $r->getHeader('host')); + $r->setPort(80); + $this->assertSame(80, $r->getPort()); + $this->assertEquals('www.foo.com', $r->getHost()); + $this->assertEquals('www.foo.com', $r->getHeader('host')); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php new file mode 100644 index 00000000..6af6f259 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php @@ -0,0 +1,140 @@ + 'hi!']); + $this->assertEquals(999, $response->getStatusCode()); + $this->assertEquals('hi!', $response->getReasonPhrase()); + } + + public function testConvertsToString() + { + $response = new Response(200); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); + // Add another header + $response = new Response(200, ['X-Test' => 'Guzzle']); + $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); + $response = new Response(200, ['Content-Length' => 4], Stream::factory('test')); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); + } + + public function testConstructorInitializesMessageWithMixedCaseHeaders() + { + $r = new Response(200, [ + 'Set-Cookie' => 'foo=bar, baz=bam', + 'Set-cookie' => 'hi=there', + 'other' => ['1', '2'] + ]); + + $this->assertEquals('foo=bar, baz=bam, hi=there', $r->getHeader('Set-Cookie')); + $this->assertEquals('1, 2', $r->getHeader('other')); + } + + public function testConvertsToStringAndSeeksToByteZero() + { + $response = new Response(200); + $s = Stream::factory('foo'); + $s->read(1); + $response->setBody($s); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\nfoo", (string) $response); + } + + public function testParsesJsonResponses() + { + $json = '{"foo": "bar"}'; + $response = new Response(200, [], Stream::factory($json)); + $this->assertEquals(['foo' => 'bar'], $response->json()); + $this->assertEquals(json_decode($json), $response->json(['object' => true])); + + $response = new Response(200); + $this->assertEquals(null, $response->json()); + } + + public function testThrowsExceptionWhenFailsToParseJsonResponse() + { + if (PHP_VERSION_ID < 70000) { + $this->setExpectedException( + 'GuzzleHttp\Exception\ParseException', + 'Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON' + ); + } else { + $this->setExpectedException( + 'GuzzleHttp\Exception\ParseException', + 'Unable to parse JSON data: JSON_ERROR_CTRL_CHAR - Unexpected control character found' + ); + } + + $response = new Response(200, [], Stream::factory('{"foo": "')); + $response->json(); + } + + public function testParsesXmlResponses() + { + $response = new Response(200, [], Stream::factory('bar')); + $this->assertEquals('bar', (string) $response->xml()->foo); + // Always return a SimpleXMLElement from the xml method + $response = new Response(200); + $this->assertEmpty((string) $response->xml()->foo); + } + + /** + * @expectedException \GuzzleHttp\Exception\XmlParseException + * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML + */ + public function testThrowsExceptionWhenFailsToParseXmlResponse() + { + $response = new Response(200, [], Stream::factory('xml(); + } catch (XmlParseException $e) { + $xmlParseError = $e->getError(); + $this->assertInstanceOf('\LibXMLError', $xmlParseError); + $this->assertContains("Couldn't find end of Start Tag abc line 1", $xmlParseError->message); + throw $e; + } + } + + public function testHasEffectiveUrl() + { + $r = new Response(200); + $this->assertNull($r->getEffectiveUrl()); + $r->setEffectiveUrl('http://www.test.com'); + $this->assertEquals('http://www.test.com', $r->getEffectiveUrl()); + } + + public function testPreventsComplexExternalEntities() + { + $xml = ']>&test;'; + $response = new Response(200, [], Stream::factory($xml)); + + $oldCwd = getcwd(); + chdir(__DIR__); + try { + $xml = $response->xml(); + chdir($oldCwd); + $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); + } catch (\Exception $e) { + chdir($oldCwd); + } + } + + public function testStatusAndReasonAreMutable() + { + $response = new Response(200); + $response->setStatusCode(201); + $this->assertEquals(201, $response->getStatusCode()); + $response->setReasonPhrase('Foo'); + $this->assertEquals('Foo', $response->getReasonPhrase()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php new file mode 100644 index 00000000..a18ec381 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php @@ -0,0 +1,31 @@ +assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); + } + + public function testGetsFromFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); + } + + public function testGetsFromCaseInsensitiveFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); + } + + public function testReturnsNullWhenNoMatchFound() + { + $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/PoolTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/PoolTest.php new file mode 100644 index 00000000..b5f02add --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/PoolTest.php @@ -0,0 +1,319 @@ + 10]); + $this->assertSame($c, $this->readAttribute($p, 'client')); + $this->assertEquals(10, $this->readAttribute($p, 'poolSize')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEachElement() + { + $c = new Client(); + $requests = ['foo']; + $p = new Pool($c, new \ArrayIterator($requests)); + $p->wait(); + } + + public function testSendsAndRealizesFuture() + { + $c = $this->getClient(); + $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); + $this->assertTrue($p->wait()); + $this->assertFalse($p->wait()); + $this->assertTrue($this->readAttribute($p, 'isRealized')); + $this->assertFalse($p->cancel()); + } + + public function testSendsManyRequestsInCappedPool() + { + $c = $this->getClient(); + $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); + $this->assertTrue($p->wait()); + $this->assertFalse($p->wait()); + } + + public function testSendsRequestsThatHaveNotBeenRealized() + { + $c = $this->getClient(); + $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); + $this->assertTrue($p->wait()); + $this->assertFalse($p->wait()); + $this->assertFalse($p->cancel()); + } + + public function testCancelsInFlightRequests() + { + $c = $this->getClient(); + $h = new History(); + $c->getEmitter()->attach($h); + $p = new Pool($c, [ + $c->createRequest('GET', 'http://foo.com'), + $c->createRequest('GET', 'http://foo.com', [ + 'events' => [ + 'before' => [ + 'fn' => function () use (&$p) { + $this->assertTrue($p->cancel()); + }, + 'priority' => RequestEvents::EARLY + ] + ] + ]) + ]); + ob_start(); + $p->wait(); + $contents = ob_get_clean(); + $this->assertEquals(1, count($h)); + $this->assertEquals('Cancelling', $contents); + } + + private function getClient() + { + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function() use ($deferred) { + $deferred->resolve(['status' => 200, 'headers' => []]); + }, function () { + echo 'Cancelling'; + } + ); + + return new Client(['handler' => new MockHandler($future)]); + } + + public function testBatchesRequests() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $responses = [ + new Response(301, ['Location' => 'http://foo.com/bar']), + new Response(200), + new Response(200), + new Response(404) + ]; + + $client->getEmitter()->attach(new Mock($responses)); + $requests = [ + $client->createRequest('GET', 'http://foo.com/baz'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + $a = $b = $c = $d = 0; + $result = Pool::batch($client, $requests, [ + 'before' => function (BeforeEvent $e) use (&$a) { $a++; }, + 'complete' => function (CompleteEvent $e) use (&$b) { $b++; }, + 'error' => function (ErrorEvent $e) use (&$c) { $c++; }, + 'end' => function (EndEvent $e) use (&$d) { $d++; } + ]); + + $this->assertEquals(4, $a); + $this->assertEquals(2, $b); + $this->assertEquals(1, $c); + $this->assertEquals(3, $d); + $this->assertCount(3, $result); + $this->assertInstanceOf('GuzzleHttp\BatchResults', $result); + + // The first result is actually the second (redirect) response. + $this->assertSame($responses[1], $result[0]); + // The second result is a 1:1 request:response map + $this->assertSame($responses[2], $result[1]); + // The third entry is the 404 RequestException + $this->assertSame($responses[3], $result[2]->getResponse()); + } + + public function testBatchesRequestsWithDynamicPoolSize() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $responses = [ + new Response(301, ['Location' => 'http://foo.com/bar']), + new Response(200), + new Response(200), + new Response(404) + ]; + + $client->getEmitter()->attach(new Mock($responses)); + $requests = [ + $client->createRequest('GET', 'http://foo.com/baz'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + $a = $b = $c = $d = 0; + $result = Pool::batch($client, $requests, [ + 'before' => function (BeforeEvent $e) use (&$a) { $a++; }, + 'complete' => function (CompleteEvent $e) use (&$b) { $b++; }, + 'error' => function (ErrorEvent $e) use (&$c) { $c++; }, + 'end' => function (EndEvent $e) use (&$d) { $d++; }, + 'pool_size' => function ($queueSize) { + static $options = [1, 2, 1]; + static $queued = 0; + + $this->assertEquals( + $queued, + $queueSize, + 'The number of queued requests should be equal to the sum of pool sizes so far.' + ); + + $next = array_shift($options); + $queued += $next; + + return $next; + } + ]); + + $this->assertEquals(4, $a); + $this->assertEquals(2, $b); + $this->assertEquals(1, $c); + $this->assertEquals(3, $d); + $this->assertCount(3, $result); + $this->assertInstanceOf('GuzzleHttp\BatchResults', $result); + + // The first result is actually the second (redirect) response. + $this->assertSame($responses[1], $result[0]); + // The second result is a 1:1 request:response map + $this->assertSame($responses[2], $result[1]); + // The third entry is the 404 RequestException + $this->assertSame($responses[3], $result[2]->getResponse()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Each event listener must be a callable or + */ + public function testBatchValidatesTheEventFormat() + { + $client = new Client(); + $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; + Pool::batch($client, $requests, ['complete' => 'foo']); + } + + public function testEmitsProgress() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $responses = [new Response(200), new Response(404)]; + $client->getEmitter()->attach(new Mock($responses)); + $requests = [ + $client->createRequest('GET', 'http://foo.com/baz'), + $client->createRequest('HEAD', 'http://httpbin.org/get') + ]; + + $pool = new Pool($client, $requests); + $count = 0; + $thenned = null; + $pool->then( + function ($value) use (&$thenned) { + $thenned = $value; + }, + null, + function ($result) use (&$count, $requests) { + $this->assertSame($requests[$count], $result['request']); + if ($count == 0) { + $this->assertNull($result['error']); + $this->assertEquals(200, $result['response']->getStatusCode()); + } else { + $this->assertInstanceOf( + 'GuzzleHttp\Exception\ClientException', + $result['error'] + ); + } + $count++; + } + ); + + $pool->wait(); + $this->assertEquals(2, $count); + $this->assertEquals(true, $thenned); + } + + public function testDoesNotThrowInErrorEvent() + { + $client = new Client(); + $responses = [new Response(404)]; + $client->getEmitter()->attach(new Mock($responses)); + $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; + $result = Pool::batch($client, $requests); + $this->assertCount(1, $result); + $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $result[0]); + } + + public function testHasSendMethod() + { + $client = new Client(); + $responses = [new Response(404)]; + $history = new History(); + $client->getEmitter()->attach($history); + $client->getEmitter()->attach(new Mock($responses)); + $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; + Pool::send($client, $requests); + $this->assertCount(1, $history); + } + + public function testDoesNotInfinitelyRecurse() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $last = null; + $client->getEmitter()->on( + 'before', + function (BeforeEvent $e) use (&$last) { + $e->intercept(new Response(200)); + if (function_exists('xdebug_get_stack_depth')) { + if ($last) { + $this->assertEquals($last, xdebug_get_stack_depth()); + } else { + $last = xdebug_get_stack_depth(); + } + } + } + ); + + $requests = []; + for ($i = 0; $i < 100; $i++) { + $requests[] = $client->createRequest('GET', 'http://foo.com'); + } + + $pool = new Pool($client, $requests); + $pool->wait(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php new file mode 100644 index 00000000..4b3b3916 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php @@ -0,0 +1,120 @@ + 'bar'], [ + new PostFile('foo', 'abc', 'foo.txt') + ], 'abcdef'); + } + + public function testConstructorAddsFieldsAndFiles() + { + $b = $this->getTestBody(); + $this->assertEquals('abcdef', $b->getBoundary()); + $c = (string) $b; + $this->assertContains("--abcdef\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n", $c); + $this->assertContains("--abcdef\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"foo.txt\"\r\n" + . "Content-Type: text/plain\r\n\r\nabc\r\n--abcdef--", $c); + } + + public function testDoesNotModifyFieldFormat() + { + $m = new MultipartBody(['foo+baz' => 'bar+bam %20 boo'], [ + new PostFile('foo+bar', 'abc %20 123', 'foo.txt') + ], 'abcdef'); + $this->assertContains('name="foo+baz"', (string) $m); + $this->assertContains('name="foo+bar"', (string) $m); + $this->assertContains('bar+bam %20 boo', (string) $m); + $this->assertContains('abc %20 123', (string) $m); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorValidatesFiles() + { + new MultipartBody([], ['bar']); + } + + public function testConstructorCanCreateBoundary() + { + $b = new MultipartBody(); + $this->assertNotNull($b->getBoundary()); + } + + public function testWrapsStreamMethods() + { + $b = $this->getTestBody(); + $this->assertFalse($b->write('foo')); + $this->assertFalse($b->isWritable()); + $this->assertTrue($b->isReadable()); + $this->assertTrue($b->isSeekable()); + $this->assertEquals(0, $b->tell()); + } + + public function testCanDetachFieldsAndFiles() + { + $b = $this->getTestBody(); + $b->detach(); + $b->close(); + $this->assertEquals('', (string) $b); + } + + public function testIsSeekableReturnsTrueIfAllAreSeekable() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable', 'isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(false)); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $p = new PostFile('foo', $s, 'foo.php'); + $b = new MultipartBody([], [$p]); + $this->assertFalse($b->isSeekable()); + $this->assertFalse($b->seek(10)); + } + + public function testReadsFromBuffer() + { + $b = $this->getTestBody(); + $c = $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $this->assertEquals('--abc', $c); + } + + public function testCalculatesSize() + { + $b = $this->getTestBody(); + $this->assertEquals(strlen($b), $b->getSize()); + } + + public function testCalculatesSizeAndReturnsNullForUnknown() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['getSize', 'isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(null)); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $b = new MultipartBody([], [new PostFile('foo', $s, 'foo.php')]); + $this->assertNull($b->getSize()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php new file mode 100644 index 00000000..0283a5eb --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php @@ -0,0 +1,255 @@ +assertTrue($b->isSeekable()); + $this->assertTrue($b->isReadable()); + $this->assertFalse($b->isWritable()); + $this->assertFalse($b->write('foo')); + } + + public function testApplyingWithNothingDoesNothing() + { + $b = new PostBody(); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertFalse($m->hasHeader('Content-Length')); + $this->assertFalse($m->hasHeader('Content-Type')); + } + + public function testCanForceMultipartUploadsWhenApplying() + { + $b = new PostBody(); + $b->forceMultipartUpload(true); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + } + + public function testApplyingWithFilesAddsMultipartUpload() + { + $b = new PostBody(); + $p = new PostFile('foo', fopen(__FILE__, 'r')); + $b->addFile($p); + $this->assertEquals([$p], $b->getFiles()); + $this->assertNull($b->getFile('missing')); + $this->assertSame($p, $b->getFile('foo')); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + } + + public function testApplyingWithFieldsAddsMultipartUpload() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $this->assertEquals(['foo' => 'bar'], $b->getFields()); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'application/x-www-form', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + } + + public function testMultipartWithNestedFields() + { + $b = new PostBody(); + $b->setField('foo', ['bar' => 'baz']); + $b->forceMultipartUpload(true); + $this->assertEquals(['foo' => ['bar' => 'baz']], $b->getFields()); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + $contents = $b->getContents(); + $this->assertContains('name="foo[bar]"', $contents); + $this->assertNotContains('name="foo"', $contents); + } + + public function testCountProvidesFieldsAndFiles() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); + $this->assertEquals(2, count($b)); + $b->clearFiles(); + $b->removeField('foo'); + $this->assertEquals(0, count($b)); + $this->assertEquals([], $b->getFiles()); + $this->assertEquals([], $b->getFields()); + } + + public function testHasFields() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->setField('baz', '123'); + $this->assertEquals('bar', $b->getField('foo')); + $this->assertEquals('123', $b->getField('baz')); + $this->assertNull($b->getField('ahh')); + $this->assertTrue($b->hasField('foo')); + $this->assertFalse($b->hasField('test')); + $b->replaceFields(['abc' => '123']); + $this->assertFalse($b->hasField('foo')); + $this->assertTrue($b->hasField('abc')); + } + + public function testConvertsFieldsToQueryStyleBody() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->setField('baz', '123'); + $this->assertEquals('foo=bar&baz=123', $b); + $this->assertEquals(15, $b->getSize()); + $b->seek(0); + $this->assertEquals('foo=bar&baz=123', $b->getContents()); + $b->seek(0); + $this->assertEquals('foo=bar&baz=123', $b->read(1000)); + $this->assertEquals(15, $b->tell()); + } + + public function testCanSpecifyQueryAggregator() + { + $b = new PostBody(); + $b->setField('foo', ['baz', 'bar']); + $this->assertEquals('foo%5B0%5D=baz&foo%5B1%5D=bar', (string) $b); + $b = new PostBody(); + $b->setField('foo', ['baz', 'bar']); + $agg = Query::duplicateAggregator(); + $b->setAggregator($agg); + $this->assertEquals('foo=baz&foo=bar', (string) $b); + } + + public function testDetachesAndCloses() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->detach(); + $b->close(); + $this->assertEquals('', $b->read(10)); + } + + public function testDetachesWhenBodyIsPresent() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->getContents(); + $b->detach(); + } + + public function testFlushAndMetadataPlaceholders() + { + $b = new PostBody(); + $this->assertEquals([], $b->getMetadata()); + $this->assertNull($b->getMetadata('foo')); + } + + public function testCreatesMultipartUploadWithMultiFields() + { + $b = new PostBody(); + $b->setField('testing', ['baz', 'bar']); + $b->setField('other', 'hi'); + $b->setField('third', 'there'); + $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); + $s = (string) $b; + $this->assertContains(file_get_contents(__FILE__), $s); + $this->assertContains('testing=bar', $s); + $this->assertContains( + 'Content-Disposition: form-data; name="third"', + $s + ); + $this->assertContains( + 'Content-Disposition: form-data; name="other"', + $s + ); + } + + public function testMultipartWithBase64Fields() + { + $b = new PostBody(); + $b->setField('foo64', '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='); + $b->forceMultipartUpload(true); + $this->assertEquals( + ['foo64' => '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='], + $b->getFields() + ); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + $contents = $b->getContents(); + $this->assertContains('name="foo64"', $contents); + $this->assertContains( + '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc=', + $contents + ); + } + + public function testMultipartWithAmpersandInValue() + { + $b = new PostBody(); + $b->setField('a', 'b&c=d'); + $b->forceMultipartUpload(true); + $this->assertEquals(['a' => 'b&c=d'], $b->getFields()); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + $contents = $b->getContents(); + $this->assertContains('name="a"', $contents); + $this->assertContains('b&c=d', $contents); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $b = new PostBody(); + $b->attach('foo'); + } + + public function testDoesNotOverwriteExistingHeaderForUrlencoded() + { + $m = new Request('POST', 'http://foo.com', [ + 'content-type' => 'application/x-www-form-urlencoded; charset=utf-8' + ]); + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->applyRequestHeaders($m); + $this->assertEquals( + 'application/x-www-form-urlencoded; charset=utf-8', + $m->getHeader('Content-Type') + ); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php new file mode 100644 index 00000000..800cee50 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php @@ -0,0 +1,61 @@ +assertInstanceOf('GuzzleHttp\Post\PostFileInterface', $p); + $this->assertEquals('hi', $p->getContent()); + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('/path/to/test.php', $p->getFilename()); + $this->assertEquals( + 'form-data; name="foo"; filename="test.php"', + $p->getHeaders()['Content-Disposition'] + ); + } + + public function testGetsFilenameFromMetadata() + { + $p = new PostFile('foo', fopen(__FILE__, 'r')); + $this->assertEquals(__FILE__, $p->getFilename()); + } + + public function testDefaultsToNameWhenNoFilenameExists() + { + $p = new PostFile('foo', 'bar'); + $this->assertEquals('foo', $p->getFilename()); + } + + public function testCreatesFromMultipartFormData() + { + $mp = new MultipartBody([], [], 'baz'); + $p = new PostFile('foo', $mp); + $this->assertEquals( + 'form-data; name="foo"', + $p->getHeaders()['Content-Disposition'] + ); + $this->assertEquals( + 'multipart/form-data; boundary=baz', + $p->getHeaders()['Content-Type'] + ); + } + + public function testCanAddHeaders() + { + $p = new PostFile('foo', Stream::factory('hi'), 'test.php', [ + 'X-Foo' => '123', + 'Content-Disposition' => 'bar' + ]); + $this->assertEquals('bar', $p->getHeaders()['Content-Disposition']); + $this->assertEquals('123', $p->getHeaders()['X-Foo']); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php new file mode 100644 index 00000000..e9075a80 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php @@ -0,0 +1,80 @@ + ['a', 'b']]], + // Can parse multi-valued items that use numeric indices + ['q[0]=a&q[1]=b', ['q' => ['a', 'b']]], + // Can parse duplicates and does not include numeric indices + ['q[]=a&q[]=b', ['q' => ['a', 'b']]], + // Ensures that the value of "q" is an array even though one value + ['q[]=a', ['q' => ['a']]], + // Does not modify "." to "_" like PHP's parse_str() + ['q.a=a&q.b=b', ['q.a' => 'a', 'q.b' => 'b']], + // Can decode %20 to " " + ['q%20a=a%20b', ['q a' => 'a b']], + // Can parse funky strings with no values by assigning each to null + ['q&a', ['q' => null, 'a' => null]], + // Does not strip trailing equal signs + ['data=abc=', ['data' => 'abc=']], + // Can store duplicates without affecting other values + ['foo=a&foo=b&?µ=c', ['foo' => ['a', 'b'], '?µ' => 'c']], + // Sets value to null when no "=" is present + ['foo', ['foo' => null]], + // Preserves "0" keys. + ['0', ['0' => null]], + // Sets the value to an empty string when "=" is present + ['0=', ['0' => '']], + // Preserves falsey keys + ['var=0', ['var' => '0']], + // Can deeply nest and store duplicate PHP values + ['a[b][c]=1&a[b][c]=2', [ + 'a' => ['b' => ['c' => ['1', '2']]] + ]], + // Can parse PHP style arrays + ['a[b]=c&a[d]=e', ['a' => ['b' => 'c', 'd' => 'e']]], + // Ensure it doesn't leave things behind with repeated values + // Can parse mult-values items + ['q=a&q=b&q=c', ['q' => ['a', 'b', 'c']]], + ]; + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesQueries($input, $output) + { + $query = Query::fromString($input); + $this->assertEquals($output, $query->toArray()); + // Normalize the input and output + $query->setEncodingType(false); + $this->assertEquals(rawurldecode($input), (string) $query); + } + + public function testConvertsPlusSymbolsToSpacesByDefault() + { + $query = Query::fromString('var=foo+bar', true); + $this->assertEquals('foo bar', $query->get('var')); + } + + public function testCanControlDecodingType() + { + $qp = new QueryParser(); + $q = new Query(); + $qp->parseInto($q, 'var=foo+bar', Query::RFC3986); + $this->assertEquals('foo+bar', $q->get('var')); + $qp->parseInto($q, 'var=foo+bar', Query::RFC1738); + $this->assertEquals('foo bar', $q->get('var')); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/QueryTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/QueryTest.php new file mode 100644 index 00000000..8b9d3448 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/QueryTest.php @@ -0,0 +1,171 @@ + 'baz', 'bar' => 'bam boozle']); + $this->assertEquals('foo=baz&bar=bam%20boozle', (string) $q); + } + + public function testCanDisableUrlEncoding() + { + $q = new Query(['bar' => 'bam boozle']); + $q->setEncodingType(false); + $this->assertEquals('bar=bam boozle', (string) $q); + } + + public function testCanSpecifyRfc1783UrlEncodingType() + { + $q = new Query(['bar abc' => 'bam boozle']); + $q->setEncodingType(Query::RFC1738); + $this->assertEquals('bar+abc=bam+boozle', (string) $q); + } + + public function testCanSpecifyRfc3986UrlEncodingType() + { + $q = new Query(['bar abc' => 'bam boozle', 'ሴ' => 'hi']); + $q->setEncodingType(Query::RFC3986); + $this->assertEquals('bar%20abc=bam%20boozle&%E1%88%B4=hi', (string) $q); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEncodingType() + { + (new Query(['bar' => 'bam boozle']))->setEncodingType('foo'); + } + + public function testAggregatesMultipleValues() + { + $q = new Query(['foo' => ['bar', 'baz']]); + $this->assertEquals('foo%5B0%5D=bar&foo%5B1%5D=baz', (string) $q); + } + + public function testCanSetAggregator() + { + $q = new Query(['foo' => ['bar', 'baz']]); + $q->setAggregator(function (array $data) { + return ['foo' => ['barANDbaz']]; + }); + $this->assertEquals('foo=barANDbaz', (string) $q); + } + + public function testAllowsMultipleValuesPerKey() + { + $q = new Query(); + $q->add('facet', 'size'); + $q->add('facet', 'width'); + $q->add('facet.field', 'foo'); + // Use the duplicate aggregator + $q->setAggregator($q::duplicateAggregator()); + $this->assertEquals('facet=size&facet=width&facet.field=foo', (string) $q); + } + + public function testAllowsZeroValues() + { + $query = new Query(array( + 'foo' => 0, + 'baz' => '0', + 'bar' => null, + 'boo' => false + )); + $this->assertEquals('foo=0&baz=0&bar&boo=', (string) $query); + } + + private $encodeData = [ + 't' => [ + 'v1' => ['a', '1'], + 'v2' => 'b', + 'v3' => ['v4' => 'c', 'v5' => 'd'] + ] + ]; + + public function testEncodesDuplicateAggregator() + { + $agg = Query::duplicateAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testDuplicateEncodesNoNumericIndices() + { + $agg = Query::duplicateAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testEncodesPhpAggregator() + { + $agg = Query::phpAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1][0]' => ['a'], + 't[v1][1]' => ['1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testPhpEncodesNoNumericIndices() + { + $agg = Query::phpAggregator(false); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1][]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testCanDisableUrlEncodingDecoding() + { + $q = Query::fromString('foo=bar+baz boo%20', false); + $this->assertEquals('bar+baz boo%20', $q['foo']); + $this->assertEquals('foo=bar+baz boo%20', (string) $q); + } + + public function testCanChangeUrlEncodingDecodingToRfc1738() + { + $q = Query::fromString('foo=bar+baz', Query::RFC1738); + $this->assertEquals('bar baz', $q['foo']); + $this->assertEquals('foo=bar+baz', (string) $q); + } + + public function testCanChangeUrlEncodingDecodingToRfc3986() + { + $q = Query::fromString('foo=bar%20baz', Query::RFC3986); + $this->assertEquals('bar baz', $q['foo']); + $this->assertEquals('foo=bar%20baz', (string) $q); + } + + public function testQueryStringsAllowSlashButDoesNotDecodeWhenDisable() + { + $q = Query::fromString('foo=bar%2Fbaz&bam=boo%20boo', Query::RFC3986); + $q->setEncodingType(false); + $this->assertEquals('foo=bar/baz&bam=boo boo', (string) $q); + } + + public function testQueryStringsAllowDecodingEncodingCompletelyDisabled() + { + $q = Query::fromString('foo=bar%2Fbaz&bam=boo boo!', false); + $this->assertEquals('foo=bar%2Fbaz&bam=boo boo!', (string) $q); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php new file mode 100644 index 00000000..dd676840 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php @@ -0,0 +1,187 @@ +mf = new MessageFactory(); + } + + public function testEmitsBeforeEventInTransition() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); + $c = false; + $t->request->getEmitter()->on('before', function (BeforeEvent $e) use (&$c) { + $c = true; + }); + $fsm($t); + $this->assertTrue($c); + } + + public function testEmitsCompleteEventInTransition() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); + $t->response = new Response(200); + $t->state = 'complete'; + $c = false; + $t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) { + $c = true; + }); + $fsm($t); + $this->assertTrue($c); + } + + public function testDoesNotEmitCompleteForFuture() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); + $deferred = new Deferred(); + $t->response = new FutureResponse($deferred->promise()); + $t->state = 'complete'; + $c = false; + $t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) { + $c = true; + }); + $fsm($t); + $this->assertFalse($c); + } + + public function testTransitionsThroughSuccessfulTransfer() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $this->addListeners($request, $calls); + $client->send($request); + $this->assertEquals(['before', 'complete', 'end'], $calls); + } + + public function testTransitionsThroughErrorsInBefore() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $client = new Client(); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $t = new Transaction($client, $request); + $calls = []; + $this->addListeners($t->request, $calls); + $t->request->getEmitter()->on('before', function (BeforeEvent $e) { + throw new \Exception('foo'); + }); + try { + $fsm($t); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertContains('foo', $t->exception->getMessage()); + $this->assertEquals(['before', 'error', 'end'], $calls); + } + } + + public function testTransitionsThroughErrorsInComplete() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $this->addListeners($request, $calls); + $request->getEmitter()->once('complete', function (CompleteEvent $e) { + throw new \Exception('foo'); + }); + try { + $client->send($request); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertContains('foo', $e->getMessage()); + $this->assertEquals(['before', 'complete', 'error', 'end'], $calls); + } + } + + public function testTransitionsThroughErrorInterception() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 404]); + }, $this->mf); + $client = new Client(); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $t = new Transaction($client, $request); + $calls = []; + $this->addListeners($t->request, $calls); + $t->request->getEmitter()->on('error', function (ErrorEvent $e) { + $e->intercept(new Response(200)); + }); + $fsm($t); + $this->assertEquals(200, $t->response->getStatusCode()); + $this->assertNull($t->exception); + $this->assertEquals(['before', 'complete', 'error', 'complete', 'end'], $calls); + } + + private function addListeners(RequestInterface $request, &$calls) + { + $request->getEmitter()->on('before', function (BeforeEvent $e) use (&$calls) { + $calls[] = 'before'; + }, RequestEvents::EARLY); + $request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$calls) { + $calls[] = 'complete'; + }, RequestEvents::EARLY); + $request->getEmitter()->on('error', function (ErrorEvent $e) use (&$calls) { + $calls[] = 'error'; + }, RequestEvents::EARLY); + $request->getEmitter()->on('end', function (EndEvent $e) use (&$calls) { + $calls[] = 'end'; + }, RequestEvents::EARLY); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage Too many state transitions + */ + public function testDetectsInfiniteLoops() + { + $client = new Client([ + 'fsm' => $fsm = new RequestFsm( + function () { + return new CompletedFutureArray(['status' => 200]); + }, + new MessageFactory(), + 3 + ) + ]); + $request = $client->createRequest('GET', 'http://foo.com:123'); + $request->getEmitter()->on('before', function () { + throw new \Exception('foo'); + }); + $request->getEmitter()->on('error', function ($e) { + $e->retry(); + }); + $client->send($request); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php new file mode 100644 index 00000000..dc26a42a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php @@ -0,0 +1,195 @@ + 'hello' + ], $stream); + $request->getConfig()->set('foo', 'bar'); + $trans = new Transaction(new Client(), $request); + $factory = new MessageFactory(); + $fsm = new RequestFsm(function () {}, new MessageFactory()); + $r = RingBridge::prepareRingRequest($trans, $factory, $fsm); + $this->assertEquals('http', $r['scheme']); + $this->assertEquals('1.1', $r['version']); + $this->assertEquals('GET', $r['http_method']); + $this->assertEquals('http://httpbin.org/get?a=b', $r['url']); + $this->assertEquals('/get', $r['uri']); + $this->assertEquals('a=b', $r['query_string']); + $this->assertEquals([ + 'Host' => ['httpbin.org'], + 'test' => ['hello'] + ], $r['headers']); + $this->assertSame($stream, $r['body']); + $this->assertEquals(['foo' => 'bar'], $r['client']); + $this->assertFalse($r['future']); + } + + public function testCreatesRingRequestsWithNullQueryString() + { + $request = new Request('GET', 'http://httpbin.org'); + $trans = new Transaction(new Client(), $request); + $factory = new MessageFactory(); + $fsm = new RequestFsm(function () {}, new MessageFactory()); + $r = RingBridge::prepareRingRequest($trans, $factory, $fsm); + $this->assertNull($r['query_string']); + $this->assertEquals('/', $r['uri']); + $this->assertEquals(['Host' => ['httpbin.org']], $r['headers']); + $this->assertNull($r['body']); + $this->assertEquals([], $r['client']); + } + + public function testAddsProgress() + { + Server::enqueue([new Response(200)]); + $client = new Client(['base_url' => Server::$url]); + $request = $client->createRequest('GET'); + $called = false; + $request->getEmitter()->on( + 'progress', + function (ProgressEvent $e) use (&$called) { + $called = true; + } + ); + $this->assertEquals(200, $client->send($request)->getStatusCode()); + $this->assertTrue($called); + } + + public function testGetsResponseProtocolVersionAndEffectiveUrlAndReason() + { + $client = new Client([ + 'handler' => new MockHandler([ + 'status' => 200, + 'reason' => 'test', + 'headers' => [], + 'version' => '1.0', + 'effective_url' => 'http://foo.com' + ]) + ]); + $request = $client->createRequest('GET', 'http://foo.com'); + $response = $client->send($request); + $this->assertEquals('1.0', $response->getProtocolVersion()); + $this->assertEquals('http://foo.com', $response->getEffectiveUrl()); + $this->assertEquals('test', $response->getReasonPhrase()); + } + + public function testGetsStreamFromResponse() + { + $res = fopen('php://temp', 'r+'); + fwrite($res, 'foo'); + rewind($res); + $client = new Client([ + 'handler' => new MockHandler([ + 'status' => 200, + 'headers' => [], + 'body' => $res + ]) + ]); + $request = $client->createRequest('GET', 'http://foo.com'); + $response = $client->send($request); + $this->assertEquals('foo', (string) $response->getBody()); + } + + public function testEmitsErrorEventOnError() + { + $client = new Client(['base_url' => 'http://127.0.0.1:123']); + $request = $client->createRequest('GET'); + $called = false; + $request->getEmitter()->on('error', function () use (&$called) { + $called = true; + }); + $request->getConfig()['timeout'] = 0.001; + $request->getConfig()['connect_timeout'] = 0.001; + try { + $client->send($request); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertSame($request, $e->getRequest()); + $this->assertContains('cURL error', $e->getMessage()); + $this->assertTrue($called); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesRingRequest() + { + RingBridge::fromRingRequest([]); + } + + public function testCreatesRequestFromRing() + { + $request = RingBridge::fromRingRequest([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => [ + 'foo' => ['bar'], + 'host' => ['foo.com'] + ], + 'body' => 'test', + 'version' => '1.0' + ]); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http://foo.com/', $request->getUrl()); + $this->assertEquals('1.0', $request->getProtocolVersion()); + $this->assertEquals('test', (string) $request->getBody()); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testCanInterceptException() + { + $client = new Client(['base_url' => 'http://127.0.0.1:123']); + $request = $client->createRequest('GET'); + $called = false; + $request->getEmitter()->on( + 'error', + function (ErrorEvent $e) use (&$called) { + $called = true; + $e->intercept(new Response(200)); + } + ); + $request->getConfig()['timeout'] = 0.001; + $request->getConfig()['connect_timeout'] = 0.001; + $this->assertEquals(200, $client->send($request)->getStatusCode()); + $this->assertTrue($called); + } + + public function testCreatesLongException() + { + $r = new Request('GET', 'http://www.google.com'); + $e = RingBridge::getNoRingResponseException($r); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + $this->assertSame($r, $e->getRequest()); + } + + public function testEnsuresResponseOrExceptionWhenCompletingResponse() + { + $trans = new Transaction(new Client(), new Request('GET', 'http://f.co')); + $f = new MessageFactory(); + $fsm = new RequestFsm(function () {}, new MessageFactory()); + try { + RingBridge::completeRingResponse($trans, [], $f, $fsm); + } catch (RequestException $e) { + $this->assertSame($trans->request, $e->getRequest()); + $this->assertContains('RingPHP', $e->getMessage()); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Server.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Server.php new file mode 100644 index 00000000..1de20e38 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Server.php @@ -0,0 +1,107 @@ +fromMessage($response); + } elseif (!($response instanceof ResponseInterface)) { + throw new \Exception('Responses must be strings or Responses'); + } + $data[] = self::convertResponse($response); + } + + TestServer::enqueue($data); + } + + /** + * Get all of the received requests + * + * @param bool $hydrate Set to TRUE to turn the messages into + * actual {@see RequestInterface} objects. If $hydrate is FALSE, + * requests will be returned as strings. + * + * @return array + * @throws \RuntimeException + */ + public static function received($hydrate = false) + { + $response = TestServer::received(); + + if ($hydrate) { + $c = new Client(); + $factory = new MessageFactory(); + $response = array_map(function($message) use ($factory, $c) { + return RingBridge::fromRingRequest($message); + }, $response); + } + + return $response; + } + + public static function flush() + { + TestServer::flush(); + } + + public static function stop() + { + TestServer::stop(); + } + + public static function wait($maxTries = 5) + { + TestServer::wait($maxTries); + } + + public static function start() + { + TestServer::start(); + } + + private static function convertResponse(Response $response) + { + $headers = array_map(function ($h) { + return implode(', ', $h); + }, $response->getHeaders()); + + return [ + 'status' => $response->getStatusCode(), + 'reason' => $response->getReasonPhrase(), + 'headers' => $headers, + 'body' => base64_encode((string) $response->getBody()) + ]; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php new file mode 100644 index 00000000..bc17e2dc --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php @@ -0,0 +1,74 @@ +getMockBuilder('GuzzleHttp\Cookie\CookieJar') + ->setMethods(array('extractCookies')) + ->getMock(); + + $mock->expects($this->exactly(1)) + ->method('extractCookies') + ->with($request, $response); + + $plugin = new Cookie($mock); + $t = new Transaction(new Client(), $request); + $t->response = $response; + $plugin->onComplete(new CompleteEvent($t)); + } + + public function testProvidesCookieJar() + { + $jar = new CookieJar(); + $plugin = new Cookie($jar); + $this->assertSame($jar, $plugin->getCookieJar()); + } + + public function testCookiesAreExtractedFromRedirectResponses() + { + $jar = new CookieJar(); + $cookie = new Cookie($jar); + $history = new History(); + $mock = new Mock([ + "HTTP/1.1 302 Moved Temporarily\r\n" . + "Set-Cookie: test=583551; Domain=www.foo.com; Expires=Wednesday, 23-Mar-2050 19:49:45 GMT; Path=/\r\n" . + "Location: /redirect\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n" + ]); + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach($cookie); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($history); + + $client->get(); + $request = $client->createRequest('GET', '/'); + $client->send($request); + + $this->assertEquals('test=583551', $request->getHeader('Cookie')); + $requests = $history->getRequests(); + // Confirm subsequent requests have the cookie. + $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); + // Confirm the redirected request has the cookie. + $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie')); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php new file mode 100644 index 00000000..d28e301c --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php @@ -0,0 +1,140 @@ +response = $response; + $e = new RequestException('foo', $request, $response); + $ev = new ErrorEvent($t, $e); + $h = new History(2); + $h->onError($ev); + // Only tracks when no response is present + $this->assertEquals([], $h->getRequests()); + } + + public function testLogsConnectionErrors() + { + $request = new Request('GET', '/'); + $t = new Transaction(new Client(), $request); + $e = new RequestException('foo', $request); + $ev = new ErrorEvent($t, $e); + $h = new History(); + $h->onError($ev); + $this->assertEquals([$request], $h->getRequests()); + } + + public function testMaintainsLimitValue() + { + $request = new Request('GET', '/'); + $response = new Response(200); + $t = new Transaction(new Client(), $request); + $t->response = $response; + $ev = new CompleteEvent($t); + $h = new History(2); + $h->onComplete($ev); + $h->onComplete($ev); + $h->onComplete($ev); + $this->assertEquals(2, count($h)); + $this->assertSame($request, $h->getLastRequest()); + $this->assertSame($response, $h->getLastResponse()); + foreach ($h as $trans) { + $this->assertInstanceOf('GuzzleHttp\Message\RequestInterface', $trans['request']); + $this->assertInstanceOf('GuzzleHttp\Message\ResponseInterface', $trans['response']); + } + return $h; + } + + /** + * @depends testMaintainsLimitValue + */ + public function testClearsHistory($h) + { + $this->assertEquals(2, count($h)); + $h->clear(); + $this->assertEquals(0, count($h)); + } + + public function testWorksWithMock() + { + $client = new Client(['base_url' => 'http://localhost/']); + $h = new History(); + $client->getEmitter()->attach($h); + $mock = new Mock([new Response(200), new Response(201), new Response(202)]); + $client->getEmitter()->attach($mock); + $request = $client->createRequest('GET', '/'); + $client->send($request); + $request->setMethod('PUT'); + $client->send($request); + $request->setMethod('POST'); + $client->send($request); + $this->assertEquals(3, count($h)); + + $result = implode("\n", array_map(function ($line) { + return strpos($line, 'User-Agent') === 0 + ? 'User-Agent:' + : trim($line); + }, explode("\n", $h))); + + $this->assertEquals("> GET / HTTP/1.1 +Host: localhost +User-Agent: + +< HTTP/1.1 200 OK + +> PUT / HTTP/1.1 +Host: localhost +User-Agent: + +< HTTP/1.1 201 Created + +> POST / HTTP/1.1 +Host: localhost +User-Agent: + +< HTTP/1.1 202 Accepted +", $result); + } + + public function testCanCastToString() + { + $client = new Client(['base_url' => 'http://localhost/']); + $h = new History(); + $client->getEmitter()->attach($h); + + $mock = new Mock(array( + new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), + new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), + new Response(200, array('Content-Length' => '2'), Stream::factory('HI')) + )); + + $client->getEmitter()->attach($mock); + $request = $client->createRequest('GET', '/'); + $client->send($request); + $this->assertEquals(3, count($h)); + + $h = str_replace("\r", '', $h); + $this->assertContains("> GET / HTTP/1.1\nHost: localhost\nUser-Agent:", $h); + $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); + $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); + $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php new file mode 100644 index 00000000..b0266340 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php @@ -0,0 +1,60 @@ +getEvent(); + $event->intercept(new Response(200)); + (new HttpError())->onComplete($event); + } + + /** + * @expectedException \GuzzleHttp\Exception\ClientException + */ + public function testThrowsClientExceptionOnFailure() + { + $event = $this->getEvent(); + $event->intercept(new Response(403)); + (new HttpError())->onComplete($event); + } + + /** + * @expectedException \GuzzleHttp\Exception\ServerException + */ + public function testThrowsServerExceptionOnFailure() + { + $event = $this->getEvent(); + $event->intercept(new Response(500)); + (new HttpError())->onComplete($event); + } + + private function getEvent() + { + return new CompleteEvent(new Transaction(new Client(), new Request('PUT', '/'))); + } + + /** + * @expectedException \GuzzleHttp\Exception\ClientException + */ + public function testFullTransaction() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(403) + ])); + $client->get('http://httpbin.org'); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php new file mode 100644 index 00000000..936edf26 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php @@ -0,0 +1,225 @@ +promise(), + function () use ($deferred, $wait) { + $deferred->resolve($wait()); + }, + $cancel + ); + } + + public function testDescribesSubscribedEvents() + { + $mock = new Mock(); + $this->assertInternalType('array', $mock->getEvents()); + } + + public function testIsCountable() + { + $plugin = new Mock(); + $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $this->assertEquals(1, count($plugin)); + } + + public function testCanClearQueue() + { + $plugin = new Mock(); + $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $plugin->clearQueue(); + $this->assertEquals(0, count($plugin)); + } + + public function testRetrievesResponsesFromFiles() + { + $tmp = tempnam('/tmp', 'tfile'); + file_put_contents($tmp, "HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n"); + $plugin = new Mock(); + $plugin->addResponse($tmp); + unlink($tmp); + $this->assertEquals(1, count($plugin)); + $q = $this->readAttribute($plugin, 'queue'); + $this->assertEquals(201, $q[0]->getStatusCode()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidResponse() + { + (new Mock())->addResponse(false); + } + + public function testAddsMockResponseToRequestFromClient() + { + $response = new Response(200); + $t = new Transaction(new Client(), new Request('GET', '/')); + $m = new Mock([$response]); + $ev = new BeforeEvent($t); + $m->onBefore($ev); + $this->assertSame($response, $t->response); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testUpdateThrowsExceptionWhenEmpty() + { + $p = new Mock(); + $ev = new BeforeEvent(new Transaction(new Client(), new Request('GET', '/'))); + $p->onBefore($ev); + } + + public function testReadsBodiesFromMockedRequests() + { + $m = new Mock([new Response(200)]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($m); + $body = Stream::factory('foo'); + $client->put('/', ['body' => $body]); + $this->assertEquals(3, $body->tell()); + } + + public function testCanMockBadRequestExceptions() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/'); + $ex = new RequestException('foo', $request); + $mock = new Mock([$ex]); + $this->assertCount(1, $mock); + $request->getEmitter()->attach($mock); + + try { + $client->send($request); + $this->fail('Did not dequeue an exception'); + } catch (RequestException $e) { + $this->assertSame($e, $ex); + $this->assertSame($request, $ex->getRequest()); + } + } + + public function testCanMockFutureResponses() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/', ['future' => true]); + $response = new Response(200); + $future = self::createFuture(function () use ($response) { + return $response; + }); + $mock = new Mock([$future]); + $this->assertCount(1, $mock); + $request->getEmitter()->attach($mock); + $res = $client->send($request); + $this->assertSame($future, $res); + $this->assertFalse($this->readAttribute($res, 'isRealized')); + $this->assertSame($response, $res->wait()); + } + + public function testCanMockExceptionFutureResponses() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/', ['future' => true]); + $future = self::createFuture(function () use ($request) { + throw new RequestException('foo', $request); + }); + + $mock = new Mock([$future]); + $request->getEmitter()->attach($mock); + $response = $client->send($request); + $this->assertSame($future, $response); + $this->assertFalse($this->readAttribute($response, 'isRealized')); + + try { + $response->wait(); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertContains('foo', $e->getMessage()); + } + } + + public function testSaveToFile() + { + $filename = sys_get_temp_dir().'/mock_test_'.uniqid(); + $file = tmpfile(); + $stream = new Stream(tmpfile()); + + $m = new Mock([ + new Response(200, [], Stream::factory('TEST FILENAME')), + new Response(200, [], Stream::factory('TEST FILE')), + new Response(200, [], Stream::factory('TEST STREAM')), + ]); + + $client = new Client(); + $client->getEmitter()->attach($m); + + $client->get('/', ['save_to' => $filename]); + $client->get('/', ['save_to' => $file]); + $client->get('/', ['save_to' => $stream]); + + $this->assertFileExists($filename); + $this->assertEquals('TEST FILENAME', file_get_contents($filename)); + + $meta = stream_get_meta_data($file); + + $this->assertFileExists($meta['uri']); + $this->assertEquals('TEST FILE', file_get_contents($meta['uri'])); + + $this->assertFileExists($stream->getMetadata('uri')); + $this->assertEquals('TEST STREAM', file_get_contents($stream->getMetadata('uri'))); + + unlink($filename); + } + + public function testCanMockFailedFutureResponses() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/', ['future' => true]); + + // The first mock will be a mocked future response. + $future = self::createFuture(function () use ($client) { + // When dereferenced, we will set a mocked response and send + // another request. + $client->get('http://httpbin.org', ['events' => [ + 'before' => function (BeforeEvent $e) { + $e->intercept(new Response(404)); + } + ]]); + }); + + $mock = new Mock([$future]); + $request->getEmitter()->attach($mock); + $response = $client->send($request); + $this->assertSame($future, $response); + $this->assertFalse($this->readAttribute($response, 'isRealized')); + + try { + $response->wait(); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertEquals(404, $e->getResponse()->getStatusCode()); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php new file mode 100644 index 00000000..d07fdb44 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php @@ -0,0 +1,213 @@ +getTrans(); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Expect')); + } + + public function testAppliesPostBody() + { + $s = new Prepare(); + $t = $this->getTrans(); + $p = $this->getMockBuilder('GuzzleHttp\Post\PostBody') + ->setMethods(['applyRequestHeaders']) + ->getMockForAbstractClass(); + $p->expects($this->once()) + ->method('applyRequestHeaders'); + $t->request->setBody($p); + $s->onBefore(new BeforeEvent($t)); + } + + public function testAddsExpectHeaderWithTrue() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', true); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('100-Continue', $t->request->getHeader('Expect')); + } + + public function testAddsExpectHeaderBySize() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', 2); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertTrue($t->request->hasHeader('Expect')); + } + + public function testDoesNotModifyExpectHeaderIfPresent() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setHeader('Expect', 'foo'); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('foo', $t->request->getHeader('Expect')); + } + + public function testDoesAddExpectHeaderWhenSetToFalse() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', false); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Expect')); + } + + public function testDoesNotAddExpectHeaderBySize() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', 10); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Expect')); + } + + public function testAddsExpectHeaderForNonSeekable() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody(new NoSeekStream(Stream::factory('foo'))); + $s->onBefore(new BeforeEvent($t)); + $this->assertTrue($t->request->hasHeader('Expect')); + } + + public function testRemovesContentLengthWhenSendingWithChunked() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody(Stream::factory('foo')); + $t->request->setHeader('Transfer-Encoding', 'chunked'); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Content-Length')); + } + + public function testUsesProvidedContentLengthAndRemovesXferEncoding() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody(Stream::factory('foo')); + $t->request->setHeader('Content-Length', '3'); + $t->request->setHeader('Transfer-Encoding', 'chunked'); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals(3, $t->request->getHeader('Content-Length')); + $this->assertFalse($t->request->hasHeader('Transfer-Encoding')); + } + + public function testSetsContentTypeIfPossibleFromStream() + { + $body = $this->getMockBody(); + $sub = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody($body); + $sub->onBefore(new BeforeEvent($t)); + $this->assertEquals( + 'image/jpeg', + $t->request->getHeader('Content-Type') + ); + $this->assertEquals(4, $t->request->getHeader('Content-Length')); + } + + public function testDoesNotOverwriteExistingContentType() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody($this->getMockBody()); + $t->request->setHeader('Content-Type', 'foo/baz'); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals( + 'foo/baz', + $t->request->getHeader('Content-Type') + ); + } + + public function testSetsContentLengthIfPossible() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody($this->getMockBody()); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals(4, $t->request->getHeader('Content-Length')); + } + + public function testSetsTransferEncodingChunkedIfNeeded() + { + $r = new Request('PUT', '/'); + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['getSize']) + ->getMockForAbstractClass(); + $s->expects($this->exactly(2)) + ->method('getSize') + ->will($this->returnValue(null)); + $r->setBody($s); + $t = $this->getTrans($r); + $s = new Prepare(); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('chunked', $r->getHeader('Transfer-Encoding')); + } + + public function testContentLengthIntegrationTest() + { + Server::flush(); + Server::enqueue([new Response(200)]); + $client = new Client(['base_url' => Server::$url]); + $this->assertEquals(200, $client->put('/', [ + 'body' => 'test' + ])->getStatusCode()); + $request = Server::received(true)[0]; + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('4', $request->getHeader('Content-Length')); + $this->assertEquals('test', (string) $request->getBody()); + } + + private function getTrans($request = null) + { + return new Transaction( + new Client(), + $request ?: new Request('PUT', '/') + ); + } + + /** + * @return \GuzzleHttp\Stream\StreamInterface + */ + private function getMockBody() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\MetadataStreamInterface') + ->setMethods(['getMetadata', 'getSize']) + ->getMockForAbstractClass(); + $s->expects($this->any()) + ->method('getMetadata') + ->with('uri') + ->will($this->returnValue('/foo/baz/bar.jpg')); + $s->expects($this->exactly(2)) + ->method('getSize') + ->will($this->returnValue(4)); + + return $s; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php new file mode 100644 index 00000000..bd12af75 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php @@ -0,0 +1,302 @@ +addMultiple([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($history); + $client->getEmitter()->attach($mock); + + $request = $client->createRequest('GET', '/foo'); + // Ensure "end" is called only once + $called = 0; + $request->getEmitter()->on('end', function () use (&$called) { + $called++; + }); + $response = $client->send($request); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + // Ensure that two requests were sent + $requests = $history->getRequests(true); + + $this->assertEquals('/foo', $requests[0]->getPath()); + $this->assertEquals('GET', $requests[0]->getMethod()); + $this->assertEquals('/redirect1', $requests[1]->getPath()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('/redirect2', $requests[2]->getPath()); + $this->assertEquals('GET', $requests[2]->getMethod()); + + $this->assertEquals(1, $called); + } + + /** + * @expectedException \GuzzleHttp\Exception\TooManyRedirectsException + * @expectedExceptionMessage Will not follow more than + */ + public function testCanLimitNumberOfRedirects() + { + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->get('http://www.example.com/foo'); + } + + public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('http://test.com/foo', [ + 'headers' => ['X-Baz' => 'bar'], + 'body' => 'testing' + ]); + + $requests = $h->getRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('GET', $requests[2]->getMethod()); + } + + public function testCanRedirectWithStrictRfcCompliance() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('/foo', [ + 'headers' => ['X-Baz' => 'bar'], + 'body' => 'testing', + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + + $requests = $h->getRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('POST', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('POST', $requests[2]->getMethod()); + } + + public function testRewindsStreamWhenRedirectingIfNeeded() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + + $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['seek', 'read', 'eof', 'tell']) + ->getMockForAbstractClass(); + $body->expects($this->once())->method('tell')->will($this->returnValue(1)); + $body->expects($this->once())->method('seek')->will($this->returnValue(true)); + $body->expects($this->any())->method('eof')->will($this->returnValue(true)); + $body->expects($this->any())->method('read')->will($this->returnValue('foo')); + $client->post('/foo', [ + 'body' => $body, + 'allow_redirects' => ['max' => 5, 'strict' => true] + ]); + } + + /** + * @expectedException \GuzzleHttp\Exception\CouldNotRewindStreamException + * @expectedExceptionMessage Unable to rewind the non-seekable request body after redirecting + */ + public function testThrowsExceptionWhenStreamCannotBeRewound() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + + $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['seek', 'read', 'eof', 'tell']) + ->getMockForAbstractClass(); + $body->expects($this->once())->method('tell')->will($this->returnValue(1)); + $body->expects($this->once())->method('seek')->will($this->returnValue(false)); + $body->expects($this->any())->method('eof')->will($this->returnValue(true)); + $body->expects($this->any())->method('read')->will($this->returnValue('foo')); + $client->post('http://example.com/foo', [ + 'body' => $body, + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + } + + public function testRedirectsCanBeDisabledPerRequest() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ])); + $response = $client->put('/', ['body' => 'test', 'allow_redirects' => false]); + $this->assertEquals(301, $response->getStatusCode()); + } + + public function testCanRedirectWithNoLeadingSlashAndQuery() + { + $h = new History(); + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect?foo=bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ])); + $client->getEmitter()->attach($h); + $client->get('?foo=bar'); + $requests = $h->getRequests(true); + $this->assertEquals('http://www.foo.com?foo=bar', $requests[0]->getUrl()); + $this->assertEquals('http://www.foo.com/redirect?foo=bar', $requests[1]->getUrl()); + } + + public function testHandlesRedirectsWithSpacesProperly() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo'); + $reqs = $h->getRequests(true); + $this->assertEquals('/redirect%201', $reqs[1]->getResource()); + } + + public function testAddsRefererWhenPossible() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); + $reqs = $h->getRequests(true); + $this->assertEquals('http://www.foo.com/foo', $reqs[1]->getHeader('Referer')); + } + + public function testDoesNotAddRefererWhenChangingProtocols() + { + $client = new Client(['base_url' => 'https://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\n" + . "Location: http://www.foo.com/foo\r\n" + . "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); + $reqs = $h->getRequests(true); + $this->assertFalse($reqs[1]->hasHeader('Referer')); + } + + public function testRedirectsWithGetOn303() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('http://test.com/foo', ['body' => 'testing']); + $requests = $h->getRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRelativeLinkBasedLatestRequest() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: http://www.bar.com\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $response = $client->get('/'); + $this->assertEquals( + 'http://www.bar.com/redirect', + $response->getEffectiveUrl() + ); + } + + public function testUpperCaseScheme() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: HTTP://www.bar.com\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $response = $client->get('/'); + $this->assertEquals( + 'http://www.bar.com', + $response->getEffectiveUrl() + ); + } + + /** + * @expectedException \GuzzleHttp\Exception\BadResponseException + * @expectedExceptionMessage Redirect URL, https://foo.com/redirect2, does not use one of the allowed redirect protocols: http + */ + public function testThrowsWhenRedirectingToInvalidUrlProtocol() + { + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: https://foo.com/redirect2\r\nContent-Length: 0\r\n\r\n" + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->get('http://www.example.com/foo', [ + 'allow_redirects' => [ + 'protocols' => ['http'] + ] + ]); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/TransactionTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/TransactionTest.php new file mode 100644 index 00000000..42965b1b --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/TransactionTest.php @@ -0,0 +1,22 @@ +assertSame($client, $t->client); + $this->assertSame($request, $t->request); + $response = new Response(200); + $t->response = $response; + $this->assertSame($response, $t->response); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php new file mode 100644 index 00000000..3f7a7f06 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php @@ -0,0 +1,202 @@ + 'value', + 'hello' => 'Hello World!', + 'empty' => '', + 'path' => '/foo/bar', + 'x' => '1024', + 'y' => '768', + 'null' => null, + 'list' => array('red', 'green', 'blue'), + 'keys' => array( + "semi" => ';', + "dot" => '.', + "comma" => ',' + ), + 'empty_keys' => array(), + ); + + return array_map(function ($t) use ($params) { + $t[] = $params; + return $t; + }, array( + array('foo', 'foo'), + array('{var}', 'value'), + array('{hello}', 'Hello%20World%21'), + array('{+var}', 'value'), + array('{+hello}', 'Hello%20World!'), + array('{+path}/here', '/foo/bar/here'), + array('here?ref={+path}', 'here?ref=/foo/bar'), + array('X{#var}', 'X#value'), + array('X{#hello}', 'X#Hello%20World!'), + array('map?{x,y}', 'map?1024,768'), + array('{x,hello,y}', '1024,Hello%20World%21,768'), + array('{+x,hello,y}', '1024,Hello%20World!,768'), + array('{+path,x}/here', '/foo/bar,1024/here'), + array('{#x,hello,y}', '#1024,Hello%20World!,768'), + array('{#path,x}/here', '#/foo/bar,1024/here'), + array('X{.var}', 'X.value'), + array('X{.x,y}', 'X.1024.768'), + array('{/var}', '/value'), + array('{/var,x}/here', '/value/1024/here'), + array('{;x,y}', ';x=1024;y=768'), + array('{;x,y,empty}', ';x=1024;y=768;empty'), + array('{?x,y}', '?x=1024&y=768'), + array('{?x,y,empty}', '?x=1024&y=768&empty='), + array('?fixed=yes{&x}', '?fixed=yes&x=1024'), + array('{&x,y,empty}', '&x=1024&y=768&empty='), + array('{var:3}', 'val'), + array('{var:30}', 'value'), + array('{list}', 'red,green,blue'), + array('{list*}', 'red,green,blue'), + array('{keys}', 'semi,%3B,dot,.,comma,%2C'), + array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), + array('{+path:6}/here', '/foo/b/here'), + array('{+list}', 'red,green,blue'), + array('{+list*}', 'red,green,blue'), + array('{+keys}', 'semi,;,dot,.,comma,,'), + array('{+keys*}', 'semi=;,dot=.,comma=,'), + array('{#path:6}/here', '#/foo/b/here'), + array('{#list}', '#red,green,blue'), + array('{#list*}', '#red,green,blue'), + array('{#keys}', '#semi,;,dot,.,comma,,'), + array('{#keys*}', '#semi=;,dot=.,comma=,'), + array('X{.var:3}', 'X.val'), + array('X{.list}', 'X.red,green,blue'), + array('X{.list*}', 'X.red.green.blue'), + array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), + array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), + array('{/var:1,var}', '/v/value'), + array('{/list}', '/red,green,blue'), + array('{/list*}', '/red/green/blue'), + array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), + array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), + array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), + array('{;hello:5}', ';hello=Hello'), + array('{;list}', ';list=red,green,blue'), + array('{;list*}', ';list=red;list=green;list=blue'), + array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), + array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), + array('{?var:3}', '?var=val'), + array('{?list}', '?list=red,green,blue'), + array('{?list*}', '?list=red&list=green&list=blue'), + array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), + array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), + array('{&var:3}', '&var=val'), + array('{&list}', '&list=red,green,blue'), + array('{&list*}', '&list=red&list=green&list=blue'), + array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), + array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), + array('{.null}', ''), + array('{.null,var}', '.value'), + array('X{.empty_keys*}', 'X'), + array('X{.empty_keys}', 'X'), + // Test that missing expansions are skipped + array('test{&missing*}', 'test'), + // Test that multiple expansions can be set + array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + // Test more complex query string stuff + array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') + )); + } + + /** + * @dataProvider templateProvider + */ + public function testExpandsUriTemplates($template, $expansion, $params) + { + $uri = new UriTemplate($template); + $this->assertEquals($expansion, $uri->expand($template, $params)); + } + + public function expressionProvider() + { + return array( + array( + '{+var*}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'var', 'modifier' => '*') + ) + ), + ), + array( + '{?keys,var,val}', array( + 'operator' => '?', + 'values' => array( + array('value' => 'keys', 'modifier' => ''), + array('value' => 'var', 'modifier' => ''), + array('value' => 'val', 'modifier' => '') + ) + ), + ), + array( + '{+x,hello,y}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'x', 'modifier' => ''), + array('value' => 'hello', 'modifier' => ''), + array('value' => 'y', 'modifier' => '') + ) + ) + ) + ); + } + + /** + * @dataProvider expressionProvider + */ + public function testParsesExpressions($exp, $data) + { + $template = new UriTemplate($exp); + + // Access the config object + $class = new \ReflectionClass($template); + $method = $class->getMethod('parseExpression'); + $method->setAccessible(true); + + $exp = substr($exp, 1, -1); + $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/90 + */ + public function testAllowsNestedArrayExpansion() + { + $template = new UriTemplate(); + + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => array('fun', 'ice cream') + ), + 'foo' => array( + 'baz' => array( + 'bar' => 'fizz', + 'test' => 'buzz' + ), + 'bam' => 'boo' + ) + )); + + $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UrlTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UrlTest.php new file mode 100644 index 00000000..22bf7e49 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UrlTest.php @@ -0,0 +1,364 @@ +assertEquals('', (string) $url); + } + + public function testPortIsDeterminedFromScheme() + { + $this->assertEquals(80, Url::fromString('http://www.test.com/')->getPort()); + $this->assertEquals(443, Url::fromString('https://www.test.com/')->getPort()); + $this->assertEquals(21, Url::fromString('ftp://www.test.com/')->getPort()); + $this->assertEquals(8192, Url::fromString('http://www.test.com:8192/')->getPort()); + $this->assertEquals(null, Url::fromString('foo://www.test.com/')->getPort()); + } + + public function testRemovesDefaultPortWhenSettingScheme() + { + $url = Url::fromString('http://www.test.com/'); + $url->setPort(80); + $url->setScheme('https'); + $this->assertEquals(443, $url->getPort()); + } + + public function testCloneCreatesNewInternalObjects() + { + $u1 = Url::fromString('http://www.test.com/'); + $u2 = clone $u1; + $this->assertNotSame($u1->getQuery(), $u2->getQuery()); + } + + public function testValidatesUrlPartsInFactory() + { + $url = Url::fromString('/index.php'); + $this->assertEquals('/index.php', (string) $url); + $this->assertFalse($url->isAbsolute()); + + $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; + $u = Url::fromString($url); + $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); + $this->assertTrue($u->isAbsolute()); + } + + public function testAllowsFalsyUrlParts() + { + $url = Url::fromString('http://a:50/0?0#0'); + $this->assertSame('a', $url->getHost()); + $this->assertEquals(50, $url->getPort()); + $this->assertSame('/0', $url->getPath()); + $this->assertEquals('0', (string) $url->getQuery()); + $this->assertSame('0', $url->getFragment()); + $this->assertEquals('http://a:50/0?0#0', (string) $url); + + $url = Url::fromString(''); + $this->assertSame('', (string) $url); + + $url = Url::fromString('0'); + $this->assertSame('0', (string) $url); + } + + public function testBuildsRelativeUrlsWithFalsyParts() + { + $url = Url::buildUrl(['path' => '/0']); + $this->assertSame('/0', $url); + + $url = Url::buildUrl(['path' => '0']); + $this->assertSame('0', $url); + + $url = Url::buildUrl(['host' => '', 'path' => '0']); + $this->assertSame('0', $url); + } + + public function testUrlStoresParts() + { + $url = Url::fromString('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); + $this->assertEquals('http', $url->getScheme()); + $this->assertEquals('test', $url->getUsername()); + $this->assertEquals('pass', $url->getPassword()); + $this->assertEquals('www.test.com', $url->getHost()); + $this->assertEquals(8081, $url->getPort()); + $this->assertEquals('/path/path2/', $url->getPath()); + $this->assertEquals('fragment', $url->getFragment()); + $this->assertEquals('a=1&b=2', (string) $url->getQuery()); + + $this->assertEquals(array( + 'fragment' => 'fragment', + 'host' => 'www.test.com', + 'pass' => 'pass', + 'path' => '/path/path2/', + 'port' => 8081, + 'query' => 'a=1&b=2', + 'scheme' => 'http', + 'user' => 'test' + ), $url->getParts()); + } + + public function testHandlesPathsCorrectly() + { + $url = Url::fromString('http://www.test.com'); + $this->assertEquals('', $url->getPath()); + $url->setPath('test'); + $this->assertEquals('test', $url->getPath()); + + $url->setPath('/test/123/abc'); + $this->assertEquals(array('', 'test', '123', 'abc'), $url->getPathSegments()); + + $parts = parse_url('http://www.test.com/test'); + $parts['path'] = ''; + $this->assertEquals('http://www.test.com', Url::buildUrl($parts)); + $parts['path'] = 'test'; + $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); + } + + public function testAddsQueryIfPresent() + { + $this->assertEquals('?foo=bar', Url::buildUrl(array( + 'query' => 'foo=bar' + ))); + } + + public function testAddsToPath() + { + // Does nothing here + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath(false); + $this->assertEquals('http://e.com/base?a=1', $url); + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath(''); + $this->assertEquals('http://e.com/base?a=1', $url); + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath('/'); + $this->assertEquals('http://e.com/base?a=1', $url); + $url = Url::fromString('http://e.com/base'); + $url->addPath('0'); + $this->assertEquals('http://e.com/base/0', $url); + + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath('relative'); + $this->assertEquals('http://e.com/base/relative?a=1', $url); + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath('/relative'); + $this->assertEquals('http://e.com/base/relative?a=1', $url); + } + + /** + * URL combination data provider + * + * @return array + */ + public function urlCombineDataProvider() + { + return [ + // Specific test cases + ['http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'], + ['http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'], + ['http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'], + ['http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'], + ['http://www.example.com/path', 'http://test.com', 'http://test.com'], + ['http://www.example.com:8080/path', 'http://test.com', 'http://test.com'], + ['http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'], + ['http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'], + ['/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'], + ['http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'], + ['https://www.example.com/path', '//foo.com/abc', 'https://foo.com/abc'], + ['https://www.example.com/0/', 'relative/foo', 'https://www.example.com/0/relative/foo'], + ['', '0', '0'], + // RFC 3986 test cases + [self::RFC3986_BASE, 'g:h', 'g:h'], + [self::RFC3986_BASE, 'g', 'http://a/b/c/g'], + [self::RFC3986_BASE, './g', 'http://a/b/c/g'], + [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], + [self::RFC3986_BASE, '/g', 'http://a/g'], + [self::RFC3986_BASE, '//g', 'http://g'], + [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'], + [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'], + [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'], + [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'], + [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'], + [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'], + [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'], + [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'], + [self::RFC3986_BASE, '', self::RFC3986_BASE], + [self::RFC3986_BASE, '.', 'http://a/b/c/'], + [self::RFC3986_BASE, './', 'http://a/b/c/'], + [self::RFC3986_BASE, '..', 'http://a/b/'], + [self::RFC3986_BASE, '../', 'http://a/b/'], + [self::RFC3986_BASE, '../g', 'http://a/b/g'], + [self::RFC3986_BASE, '../..', 'http://a/'], + [self::RFC3986_BASE, '../../', 'http://a/'], + [self::RFC3986_BASE, '../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../../g', 'http://a/g'], + [self::RFC3986_BASE, '/./g', 'http://a/g'], + [self::RFC3986_BASE, '/../g', 'http://a/g'], + [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'], + [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'], + [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'], + [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'], + [self::RFC3986_BASE, './../g', 'http://a/b/g'], + [self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'], + [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'], + [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'], + [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'], + [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'], + [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'], + [self::RFC3986_BASE, 'http:g', 'http:g'], + ]; + } + + /** + * @dataProvider urlCombineDataProvider + */ + public function testCombinesUrls($a, $b, $c) + { + $this->assertEquals($c, (string) Url::fromString($a)->combine($b)); + } + + public function testHasGettersAndSetters() + { + $url = Url::fromString('http://www.test.com/'); + $url->setHost('example.com'); + $this->assertEquals('example.com', $url->getHost()); + $url->setPort(8080); + $this->assertEquals('8080', $url->getPort()); + $url->setPath('/foo/bar'); + $this->assertEquals('/foo/bar', $url->getPath()); + $url->setPassword('a'); + $this->assertEquals('a', $url->getPassword()); + $url->setUsername('b'); + $this->assertEquals('b', $url->getUsername()); + $url->setFragment('abc'); + $this->assertEquals('abc', $url->getFragment()); + $url->setScheme('https'); + $this->assertEquals('https', $url->getScheme()); + $url->setQuery('a=123'); + $this->assertEquals('a=123', (string) $url->getQuery()); + $this->assertEquals( + 'https://b:a@example.com:8080/foo/bar?a=123#abc', + (string) $url + ); + $url->setQuery(new Query(['b' => 'boo'])); + $this->assertEquals('b=boo', $url->getQuery()); + $this->assertEquals( + 'https://b:a@example.com:8080/foo/bar?b=boo#abc', + (string) $url + ); + + $url->setQuery('a%20=bar!', true); + $this->assertEquals( + 'https://b:a@example.com:8080/foo/bar?a%20=bar!#abc', + (string) $url + ); + } + + public function testSetQueryAcceptsArray() + { + $url = Url::fromString('http://www.test.com'); + $url->setQuery(array('a' => 'b')); + $this->assertEquals('http://www.test.com?a=b', (string) $url); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testQueryMustBeValid() + { + $url = Url::fromString('http://www.test.com'); + $url->setQuery(false); + } + + public function testDefersParsingAndEncodingQueryUntilNecessary() + { + $url = Url::fromString('http://www.test.com'); + // Note that invalid characters are encoded. + $url->setQuery('foo#bar/', true); + $this->assertEquals('http://www.test.com?foo%23bar/', (string) $url); + $this->assertInternalType('string', $this->readAttribute($url, 'query')); + $this->assertEquals('foo%23bar%2F', (string) $url->getQuery()); + $this->assertInstanceOf('GuzzleHttp\Query', $this->readAttribute($url, 'query')); + } + + public function urlProvider() + { + return array( + array('/foo/..', '/'), + array('//foo//..', '//foo/'), + array('/foo//', '/foo//'), + array('/foo/../..', '/'), + array('/foo/../.', '/'), + array('/./foo/..', '/'), + array('/./foo', '/foo'), + array('/./foo/', '/foo/'), + array('*', '*'), + array('/foo', '/foo'), + array('/abc/123/../foo/', '/abc/foo/'), + array('/a/b/c/./../../g', '/a/g'), + array('/b/c/./../../g', '/g'), + array('/b/c/./../../g', '/g'), + array('/c/./../../g', '/g'), + array('/./../../g', '/g'), + array('foo', 'foo'), + ); + } + + /** + * @dataProvider urlProvider + */ + public function testRemoveDotSegments($path, $result) + { + $url = Url::fromString('http://www.example.com'); + $url->setPath($path); + $url->removeDotSegments(); + $this->assertEquals($result, $url->getPath()); + } + + public function testSettingHostWithPortModifiesPort() + { + $url = Url::fromString('http://www.example.com'); + $url->setHost('foo:8983'); + $this->assertEquals('foo', $url->getHost()); + $this->assertEquals(8983, $url->getPort()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesUrlCanBeParsed() + { + Url::fromString('foo:////'); + } + + public function testConvertsSpecialCharsInPathWhenCastingToString() + { + $url = Url::fromString('http://foo.com/baz bar?a=b'); + $url->addPath('?'); + $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); + } + + public function testCorrectlyEncodesPathWithoutDoubleEncoding() + { + $url = Url::fromString('http://foo.com/baz%20 bar:boo/baz!'); + $this->assertEquals('/baz%20%20bar:boo/baz!', $url->getPath()); + } + + public function testLowercaseScheme() + { + $url = Url::fromString('HTTP://foo.com/'); + $this->assertEquals('http', $url->getScheme()); + $url->setScheme('HTTPS'); + $this->assertEquals('https', $url->getScheme()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UtilsTest.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UtilsTest.php new file mode 100644 index 00000000..10bdc545 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/UtilsTest.php @@ -0,0 +1,40 @@ +assertEquals( + 'foo/123', + Utils::uriTemplate('foo/{bar}', ['bar' => '123']) + ); + } + + public function noBodyProvider() + { + return [['get'], ['head'], ['delete']]; + } + + public function testJsonDecodes() + { + $this->assertTrue(Utils::jsonDecode('true')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON + */ + public function testJsonDecodesWithErrorMessages() + { + Utils::jsonDecode('!narf!'); + } + + public function testProvidesDefaultUserAgent() + { + $ua = Utils::getDefaultUserAgent(); + $this->assertEquals(1, preg_match('#^Guzzle/.+ curl/.+ PHP/.+$#', $ua)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/bootstrap.php b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/bootstrap.php new file mode 100644 index 00000000..8713f962 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/guzzle/tests/bootstrap.php @@ -0,0 +1,11 @@ + Server::$url]); + +$t = microtime(true); +for ($i = 0; $i < $total; $i++) { + $client->get('/guzzle-server/perf'); +} +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Serial: %f (%f ms / request) %d total\n", + $totalTime, $perRequest, $total); + +// Create a generator used to yield batches of requests +$reqs = function () use ($client, $total) { + for ($i = 0; $i < $total; $i++) { + yield $client->createRequest('GET', '/guzzle-server/perf'); + } +}; + +$t = microtime(true); +Pool::send($client, $reqs(), ['parallel' => $parallel]); +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Batch: %f (%f ms / request) %d total with %d in parallel\n", + $totalTime, $perRequest, $total, $parallel); + +$handler = new CurlMultiHandler(['max_handles' => $parallel]); +$client = new Client(['handler' => $handler, 'base_url' => Server::$url]); +$t = microtime(true); +for ($i = 0; $i < $total; $i++) { + $client->get('/guzzle-server/perf'); +} +unset($client); +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Future: %f (%f ms / request) %d total\n", + $totalTime, $perRequest, $total); diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/CHANGELOG.md b/lib/Ebanx/vendor/guzzlehttp/ringphp/CHANGELOG.md new file mode 100644 index 00000000..d399d826 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/CHANGELOG.md @@ -0,0 +1,54 @@ +# CHANGELOG + +## 1.1.0 - 2015-05-19 + +* Added `CURL_HTTP_VERSION_2_0` +* The PHP stream wrapper handler now sets `allow_self_signed` to `false` to + match the cURL handler when `verify` is set to `true` or a certificate file. +* Ensuring that a directory exists before using the `save_to` option. +* Response protocol version is now correctly extracted from a response. +* Fixed a bug in which the result of `CurlFactory::retryFailedRewind` did not + return an array. + +## 1.0.7 - 2015-03-29 + +* PHP 7 fixes. + +## 1.0.6 - 2015-02-26 + +* Bug fix: futures now extend from React's PromiseInterface to ensure that they + are properly forwarded down the promise chain. +* The multi handle of the CurlMultiHandler is now created lazily. + +## 1.0.5 - 2014-12-10 + +* Adding more error information to PHP stream wrapper exceptions. +* Added digest auth integration test support to test server. + +## 1.0.4 - 2014-12-01 + +* Added support for older versions of cURL that do not have CURLOPT_TIMEOUT_MS. +* Setting debug to `false` does not enable debug output. +* Added a fix to the StreamHandler to return a `FutureArrayInterface` when an + error occurs. + +## 1.0.3 - 2014-11-03 + +* Setting the `header` stream option as a string to be compatible with GAE. +* Header parsing now ensures that header order is maintained in the parsed + message. + +## 1.0.2 - 2014-10-28 + +* Now correctly honoring a `version` option is supplied in a request. + See https://github.com/guzzle/RingPHP/pull/8 + +## 1.0.1 - 2014-10-26 + +* Fixed a header parsing issue with the `CurlHandler` and `CurlMultiHandler` + that caused cURL requests with multiple responses to merge repsonses together + (e.g., requests with digest authentication). + +## 1.0.0 - 2014-10-12 + +* Initial release. diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/LICENSE b/lib/Ebanx/vendor/guzzlehttp/ringphp/LICENSE new file mode 100644 index 00000000..71d3b783 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/Makefile b/lib/Ebanx/vendor/guzzlehttp/ringphp/Makefile new file mode 100644 index 00000000..21c812e3 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/Makefile @@ -0,0 +1,46 @@ +all: clean coverage docs + +docs: + cd docs && make html + +view-docs: + open docs/_build/html/index.html + +start-server: stop-server + node tests/Client/server.js &> /dev/null & + +stop-server: + @PID=$(shell ps axo pid,command \ + | grep 'tests/Client/server.js' \ + | grep -v grep \ + | cut -f 1 -d " "\ + ) && [ -n "$$PID" ] && kill $$PID || true + +test: start-server + vendor/bin/phpunit $(TEST) + $(MAKE) stop-server + +coverage: start-server + vendor/bin/phpunit --coverage-html=build/artifacts/coverage $(TEST) + $(MAKE) stop-server + +view-coverage: + open build/artifacts/coverage/index.html + +clean: + rm -rf build/artifacts/* + cd docs && make clean + +tag: + $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) + @echo Tagging $(TAG) + chag update -m '$(TAG) ()' + git add -A + git commit -m '$(TAG) release' + chag tag + +perf: start-server + php tests/perf.php + $(MAKE) stop-server + +.PHONY: docs diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/README.rst b/lib/Ebanx/vendor/guzzlehttp/ringphp/README.rst new file mode 100644 index 00000000..10374e81 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/README.rst @@ -0,0 +1,46 @@ +======= +RingPHP +======= + +Provides a simple API and specification that abstracts away the details of HTTP +into a single PHP function. RingPHP be used to power HTTP clients and servers +through a PHP function that accepts a request hash and returns a response hash +that is fulfilled using a `promise `_, +allowing RingPHP to support both synchronous and asynchronous workflows. + +By abstracting the implementation details of different HTTP clients and +servers, RingPHP allows you to utilize pluggable HTTP clients and servers +without tying your application to a specific implementation. + +.. code-block:: php + + 'GET', + 'uri' => '/', + 'headers' => [ + 'host' => ['www.google.com'], + 'x-foo' => ['baz'] + ] + ]); + + $response->then(function (array $response) { + echo $response['status']; + }); + + $response->wait(); + +RingPHP is inspired by Clojure's `Ring `_, +which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is +utilized as the handler layer in `Guzzle `_ 5.0+ to send +HTTP requests. + +Documentation +------------- + +See http://ringphp.readthedocs.org/ for the full online documentation. diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/composer.json b/lib/Ebanx/vendor/guzzlehttp/ringphp/composer.json new file mode 100644 index 00000000..8df60ecc --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/composer.json @@ -0,0 +1,43 @@ +{ + "name": "guzzlehttp/ringphp", + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0", + "guzzlehttp/streams": "~3.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\Ring\\": "tests/" + } + }, + "scripts": { + "test": "make test", + "test-ci": "make coverage" + }, + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/Makefile b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/Makefile new file mode 100644 index 00000000..51270aa5 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GuzzleRing.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GuzzleRing.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/GuzzleRing" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GuzzleRing" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/client_handlers.rst b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/client_handlers.rst new file mode 100644 index 00000000..3151f002 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/client_handlers.rst @@ -0,0 +1,173 @@ +=============== +Client Handlers +=============== + +Client handlers accept a request array and return a future response array that +can be used synchronously as an array or asynchronously using a promise. + +Built-In Handlers +----------------- + +RingPHP comes with three built-in client handlers. + +Stream Handler +~~~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\StreamHandler`` uses PHP's +`http stream wrapper `_ to send +requests. + +.. note:: + + This handler cannot send requests concurrently. + +You can provide an associative array of custom stream context options to the +StreamHandler using the ``stream_context`` key of the ``client`` request +option. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\StreamHandler; + + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['httpbin.org']], + 'client' => [ + 'stream_context' => [ + 'http' => [ + 'request_fulluri' => true, + 'method' => 'HEAD' + ], + 'socket' => [ + 'bindto' => '127.0.0.1:0' + ], + 'ssl' => [ + 'verify_peer' => false + ] + ] + ] + ]); + + // Even though it's already completed, you can still use a promise + $response->then(function ($response) { + echo $response['status']; // 200 + }); + + // Or access the response using the future interface + echo $response['status']; // 200 + +cURL Handler +~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\CurlHandler`` can be used with PHP 5.5+ to send +requests using cURL easy handles. This handler is great for sending requests +one at a time because the execute and select loop is implemented in C code +which executes faster and consumes less memory than using PHP's +``curl_multi_*`` interface. + +.. note:: + + This handler cannot send requests concurrently. + +When using the CurlHandler, custom curl options can be specified as an +associative array of `cURL option constants `_ +mapping to values in the ``client`` option of a requst using the **curl** key. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + + $handler = new CurlHandler(); + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]] + ]; + + $response = $handler($request); + + // The response can be used directly as an array. + echo $response['status']; // 200 + + // Or, it can be used as a promise (that has already fulfilled). + $response->then(function ($response) { + echo $response['status']; // 200 + }); + +cURL Multi Handler +~~~~~~~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\CurlMultiHandler`` transfers requests using +cURL's `multi API `_. The +``CurlMultiHandler`` is great for sending requests concurrently. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $handler = new CurlMultiHandler(); + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]] + ]; + + // this call returns a future array immediately. + $response = $handler($request); + + // Ideally, you should use the promise API to not block. + $response + ->then(function ($response) { + // Got the response at some point in the future + echo $response['status']; // 200 + // Don't break the chain + return $response; + })->then(function ($response) { + // ... + }); + + // If you really need to block, then you can use the response as an + // associative array. This will block until it has completed. + echo $response['status']; // 200 + +Just like the ``CurlHandler``, the ``CurlMultiHandler`` accepts custom curl +option in the ``curl`` key of the ``client`` request option. + +Mock Handler +~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\MockHandler`` is used to return mock responses. +When constructed, the handler can be configured to return the same response +array over and over, a future response, or a the evaluation of a callback +function. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\MockHandler; + + // Return a canned response. + $mock = new MockHandler(['status' => 200]); + $response = $mock([]); + assert(200 == $response['status']); + assert([] == $response['headers']); + +Implementing Handlers +--------------------- + +Client handlers are just PHP callables (functions or classes that have the +``__invoke`` magic method). The callable accepts a request array and MUST +return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface`` so that +the response can be used by both blocking and non-blocking consumers. + +Handlers need to follow a few simple rules: + +1. Do not throw exceptions. If an error is encountered, return an array that + contains the ``error`` key that maps to an ``\Exception`` value. +2. If the request has a ``delay`` client option, then the handler should only + send the request after the specified delay time in seconds. Blocking + handlers may find it convenient to just let the + ``GuzzleHttp\Ring\Core::doSleep($request)`` function handle this for them. +3. Always return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface``. +4. Complete any outstanding requests when the handler is destructed. diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/client_middleware.rst b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/client_middleware.rst new file mode 100644 index 00000000..5a2c1a8a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/client_middleware.rst @@ -0,0 +1,165 @@ +================= +Client Middleware +================= + +Middleware intercepts requests before they are sent over the wire and can be +used to add functionality to handlers. + +Modifying Requests +------------------ + +Let's say you wanted to modify requests before they are sent over the wire +so that they always add specific headers. This can be accomplished by creating +a function that accepts a handler and returns a new function that adds the +composed behavior. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + + $handler = new CurlHandler(); + + $addHeaderHandler = function (callable $handler, array $headers = []) { + return function (array $request) use ($handler, $headers) { + // Add our custom headers + foreach ($headers as $key => $value) { + $request['headers'][$key] = $value; + } + + // Send the request using the handler and return the response. + return $handler($request); + } + }; + + // Create a new handler that adds headers to each request. + $handler = $addHeaderHandler($handler, [ + 'X-AddMe' => 'hello', + 'Authorization' => 'Basic xyz' + ]); + + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['httpbin.org']] + ]); + +Modifying Responses +------------------- + +You can change a response as it's returned from a middleware. Remember that +responses returned from an handler (including middleware) must implement +``GuzzleHttp\Ring\Future\FutureArrayInterface``. In order to be a good citizen, +you should not expect that the responses returned through your middleware will +be completed synchronously. Instead, you should use the +``GuzzleHttp\Ring\Core::proxy()`` function to modify the response when the +underlying promise is resolved. This function is a helper function that makes it +easy to create a new instance of ``FutureArrayInterface`` that wraps an existing +``FutureArrayInterface`` object. + +Let's say you wanted to add headers to a response as they are returned from +your middleware, but you want to make sure you aren't causing future +responses to be dereferenced right away. You can achieve this by modifying the +incoming request and using the ``Core::proxy`` function. + +.. code-block:: php + + use GuzzleHttp\Ring\Core; + use GuzzleHttp\Ring\Client\CurlHandler; + + $handler = new CurlHandler(); + + $responseHeaderHandler = function (callable $handler, array $headers) { + return function (array $request) use ($handler, $headers) { + // Send the request using the wrapped handler. + return Core::proxy($handler($request), function ($response) use ($headers) { + // Add the headers to the response when it is available. + foreach ($headers as $key => $value) { + $response['headers'][$key] = (array) $value; + } + // Note that you can return a regular response array when using + // the proxy method. + return $response; + }); + } + }; + + // Create a new handler that adds headers to each response. + $handler = $responseHeaderHandler($handler, ['X-Header' => 'hello!']); + + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['httpbin.org']] + ]); + + assert($response['headers']['X-Header'] == 'hello!'); + +Built-In Middleware +------------------- + +RingPHP comes with a few basic client middlewares that modify requests +and responses. + +Streaming Middleware +~~~~~~~~~~~~~~~~~~~~ + +If you want to send all requests with the ``streaming`` option to a specific +handler but other requests to a different handler, then use the streaming +middleware. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + use GuzzleHttp\Ring\Client\StreamHandler; + use GuzzleHttp\Ring\Client\Middleware; + + $defaultHandler = new CurlHandler(); + $streamingHandler = new StreamHandler(); + $streamingHandler = Middleware::wrapStreaming( + $defaultHandler, + $streamingHandler + ); + + // Send the request using the streaming handler. + $response = $streamingHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']], + 'stream' => true + ]); + + // Send the request using the default handler. + $response = $streamingHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']] + ]); + +Future Middleware +~~~~~~~~~~~~~~~~~ + +If you want to send all requests with the ``future`` option to a specific +handler but other requests to a different handler, then use the future +middleware. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + use GuzzleHttp\Ring\Client\CurlMultiHandler; + use GuzzleHttp\Ring\Client\Middleware; + + $defaultHandler = new CurlHandler(); + $futureHandler = new CurlMultiHandler(); + $futureHandler = Middleware::wrapFuture( + $defaultHandler, + $futureHandler + ); + + // Send the request using the blocking CurlHandler. + $response = $futureHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']] + ]); + + // Send the request using the non-blocking CurlMultiHandler. + $response = $futureHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']], + 'future' => true + ]); diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/conf.py b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/conf.py new file mode 100644 index 00000000..c6404aa1 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/conf.py @@ -0,0 +1,23 @@ +import sys, os +import sphinx_rtd_theme +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'RingPHP' +copyright = u'2014, Michael Dowling' +version = '1.0.0-alpha' +exclude_patterns = ['_build'] + +html_title = "RingPHP" +html_short_title = "RingPHP" +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/futures.rst b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/futures.rst new file mode 100644 index 00000000..af29cb37 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/futures.rst @@ -0,0 +1,164 @@ +======= +Futures +======= + +Futures represent a computation that may have not yet completed. RingPHP +uses hybrid of futures and promises to provide a consistent API that can be +used for both blocking and non-blocking consumers. + +Promises +-------- + +You can get the result of a future when it is ready using the promise interface +of a future. Futures expose a promise API via a ``then()`` method that utilizes +`React's promise library `_. You should +use this API when you do not wish to block. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $request = [ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['httpbin.org']] + ]; + + $response = $handler($request); + + // Use the then() method to use the promise API of the future. + $response->then(function ($response) { + echo $response['status']; + }); + +You can get the promise used by a future, an instance of +``React\Promise\PromiseInterface``, by calling the ``promise()`` method. + +.. code-block:: php + + $response = $handler($request); + $promise = $response->promise(); + $promise->then(function ($response) { + echo $response['status']; + }); + +This promise value can be used with React's +`aggregate promise functions `_. + +Waiting +------- + +You can wait on a future to complete and retrieve the value, or *dereference* +the future, using the ``wait()`` method. Calling the ``wait()`` method of a +future will block until the result is available. The result is then returned or +an exception is thrown if and exception was encountered while waiting on the +the result. Subsequent calls to dereference a future will return the previously +completed result or throw the previously encountered exception. Futures can be +cancelled, which stops the computation if possible. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['httpbin.org']] + ]); + + // You can explicitly call block to wait on a result. + $realizedResponse = $response->wait(); + + // Future responses can be used like a regular PHP array. + echo $response['status']; + +In addition to explicitly calling the ``wait()`` function, using a future like +a normal value will implicitly trigger the ``wait()`` function. + +Future Responses +---------------- + +RingPHP uses futures to return asynchronous responses immediately. Client +handlers always return future responses that implement +``GuzzleHttp\Ring\Future\ArrayFutureInterface``. These future responses act +just like normal PHP associative arrays for blocking access and provide a +promise interface for non-blocking access. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $handler = new CurlMultiHandler(); + + $request = [ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['Host' => ['www.google.com']] + ]; + + $response = $handler($request); + + // Use the promise API for non-blocking access to the response. The actual + // response value will be delivered to the promise. + $response->then(function ($response) { + echo $response['status']; + }); + + // You can wait (block) until the future is completed. + $response->wait(); + + // This will implicitly call wait(), and will block too! + $response['status']; + +.. important:: + + Futures that are not completed by the time the underlying handler is + destructed will be completed when the handler is shutting down. + +Cancelling +---------- + +Futures can be cancelled if they have not already been dereferenced. + +RingPHP futures are typically implemented with the +``GuzzleHttp\Ring\Future\BaseFutureTrait``. This trait provides the cancellation +functionality that should be common to most implementations. Cancelling a +future response will try to prevent the request from sending over the wire. + +When a future is cancelled, the cancellation function is invoked and performs +the actual work needed to cancel the request from sending if possible +(e.g., telling an event loop to stop sending a request or to close a socket). +If no cancellation function is provided, then a request cannot be cancelled. If +a cancel function is provided, then it should accept the future as an argument +and return true if the future was successfully cancelled or false if it could +not be cancelled. + +Wrapping an existing Promise +---------------------------- + +You can easily create a future from any existing promise using the +``GuzzleHttp\Ring\Future\FutureValue`` class. This class's constructor +accepts a promise as the first argument, a wait function as the second +argument, and a cancellation function as the third argument. The dereference +function is used to force the promise to resolve (for example, manually ticking +an event loop). The cancel function is optional and is used to tell the thing +that created the promise that it can stop computing the result (for example, +telling an event loop to stop transferring a request). + +.. code-block:: php + + use GuzzleHttp\Ring\Future\FutureValue; + use React\Promise\Deferred; + + $deferred = new Deferred(); + $promise = $deferred->promise(); + + $f = new FutureValue( + $promise, + function () use ($deferred) { + // This function is responsible for blocking and resolving the + // promise. Here we pass in a reference to the deferred so that + // it can be resolved or rejected. + $deferred->resolve('foo'); + } + ); diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/index.rst b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/index.rst new file mode 100644 index 00000000..4bbce631 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/index.rst @@ -0,0 +1,50 @@ +======= +RingPHP +======= + +Provides a simple API and specification that abstracts away the details of HTTP +into a single PHP function. RingPHP be used to power HTTP clients and servers +through a PHP function that accepts a request hash and returns a response hash +that is fulfilled using a `promise `_, +allowing RingPHP to support both synchronous and asynchronous workflows. + +By abstracting the implementation details of different HTTP clients and +servers, RingPHP allows you to utilize pluggable HTTP clients and servers +without tying your application to a specific implementation. + +.. toctree:: + :maxdepth: 2 + + spec + futures + client_middleware + client_handlers + testing + +.. code-block:: php + + 'GET', + 'uri' => '/', + 'headers' => [ + 'host' => ['www.google.com'], + 'x-foo' => ['baz'] + ] + ]); + + $response->then(function (array $response) { + echo $response['status']; + }); + + $response->wait(); + +RingPHP is inspired by Clojure's `Ring `_, +which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is +utilized as the handler layer in `Guzzle `_ 5.0+ to send +HTTP requests. diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/requirements.txt b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/requirements.txt new file mode 100644 index 00000000..483a4e96 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/requirements.txt @@ -0,0 +1 @@ +sphinx_rtd_theme diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/spec.rst b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/spec.rst new file mode 100644 index 00000000..bc910789 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/spec.rst @@ -0,0 +1,311 @@ +============= +Specification +============= + +RingPHP applications consist of handlers, requests, responses, and +middleware. + +Handlers +-------- + +Handlers are implemented as a PHP ``callable`` that accept a request array +and return a response array (``GuzzleHttp\Ring\Future\FutureArrayInterface``). + +For example: + +.. code-block:: php + + use GuzzleHttp\Ring\Future\CompletedFutureArray; + + $mockHandler = function (array $request) { + return new CompletedFutureArray([ + 'status' => 200, + 'headers' => ['X-Foo' => ['Bar']], + 'body' => 'Hello!' + ]); + }; + +This handler returns the same response each time it is invoked. All RingPHP +handlers must return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. Use +``GuzzleHttp\Ring\Future\CompletedFutureArray`` when returning a response that +has already completed. + +Requests +-------- + +A request array is a PHP associative array that contains the configuration +settings need to send a request. + +.. code-block:: php + + $request = [ + 'http_method' => 'GET', + 'scheme' => 'http', + 'uri' => '/', + 'body' => 'hello!', + 'client' => ['timeout' => 1.0], + 'headers' => [ + 'host' => ['httpbin.org'], + 'X-Foo' => ['baz', 'bar'] + ] + ]; + +The request array contains the following key value pairs: + +request_method + (string, required) The HTTP request method, must be all caps corresponding + to a HTTP request method, such as ``GET`` or ``POST``. + +scheme + (string) The transport protocol, must be one of ``http`` or ``https``. + Defaults to ``http``. + +uri + (string, required) The request URI excluding the query string. Must + start with "/". + +query_string + (string) The query string, if present (e.g., ``foo=bar``). + +version + (string) HTTP protocol version. Defaults to ``1.1``. + +headers + (required, array) Associative array of headers. Each key represents the + header name. Each value contains an array of strings where each entry of + the array SHOULD be sent over the wire on a separate header line. + +body + (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) + The body of the request, if present. Can be a string, resource returned + from fopen, an ``Iterator`` that yields chunks of data, an object that + implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. + +future + (bool, string) Controls the asynchronous behavior of a response. + + Set to ``true`` or omit the ``future`` option to *request* that a request + will be completed asynchronously. Keep in mind that your request might not + necessarily be completed asynchronously based on the handler you are using. + Set the ``future`` option to ``false`` to request that a synchronous + response be provided. + + You can provide a string value to specify fine-tuned future behaviors that + may be specific to the underlying handlers you are using. There are, + however, some common future options that handlers should implement if + possible. + + lazy + Requests that the handler does not open and send the request + immediately, but rather only opens and sends the request once the + future is dereferenced. This option is often useful for sending a large + number of requests concurrently to allow handlers to take better + advantage of non-blocking transfers by first building up a pool of + requests. + + If an handler does not implement or understand a provided string value, + then the request MUST be treated as if the user provided ``true`` rather + than the string value. + + Future responses created by asynchronous handlers MUST attempt to complete + any outstanding future responses when they are destructed. Asynchronous + handlers MAY choose to automatically complete responses when the number + of outstanding requests reaches an handler-specific threshold. + +Client Specific Options +~~~~~~~~~~~~~~~~~~~~~~~ + +The following options are only used in ring client handlers. + +.. _client-options: + +client + (array) Associative array of client specific transfer options. The + ``client`` request key value pair can contain the following keys: + + cert + (string, array) Set to a string to specify the path to a file + containing a PEM formatted SSL client side certificate. If a password + is required, then set ``cert`` to an array containing the path to the + PEM file in the first array element followed by the certificate + password in the second array element. + + connect_timeout + (float) Float describing the number of seconds to wait while trying to + connect to a server. Use ``0`` to wait indefinitely (the default + behavior). + + debug + (bool, fopen() resource) Set to true or set to a PHP stream returned by + fopen() to enable debug output with the handler used to send a request. + If set to ``true``, the output is written to PHP's STDOUT. If a PHP + ``fopen`` resource handle is provided, the output is written to the + stream. + + "Debug output" is handler specific: different handlers will yield + different output and various various level of detail. For example, when + using cURL to transfer requests, cURL's `CURLOPT_VERBOSE `_ + will be used. When using the PHP stream wrapper, `stream notifications `_ + will be emitted. + + decode_content + (bool) Specify whether or not ``Content-Encoding`` responses + (gzip, deflate, etc.) are automatically decoded. Set to ``true`` to + automatically decode encoded responses. Set to ``false`` to not decode + responses. By default, content is *not* decoded automatically. + + delay + (int) The number of milliseconds to delay before sending the request. + This is often used for delaying before retrying a request. Handlers + SHOULD implement this if possible, but it is not a strict requirement. + + progress + (function) Defines a function to invoke when transfer progress is made. + The function accepts the following arguments: + + 1. The total number of bytes expected to be downloaded + 2. The number of bytes downloaded so far + 3. The number of bytes expected to be uploaded + 4. The number of bytes uploaded so far + + proxy + (string, array) Pass a string to specify an HTTP proxy, or an + associative array to specify different proxies for different protocols + where the scheme is the key and the value is the proxy address. + + .. code-block:: php + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => ['httpbin.org']], + 'client' => [ + // Use different proxies for different URI schemes. + 'proxy' => [ + 'http' => 'http://proxy.example.com:5100', + 'https' => 'https://proxy.example.com:6100' + ] + ] + ]; + + ssl_key + (string, array) Specify the path to a file containing a private SSL key + in PEM format. If a password is required, then set to an array + containing the path to the SSL key in the first array element followed + by the password required for the certificate in the second element. + + save_to + (string, fopen resource, ``GuzzleHttp\Stream\StreamInterface``) + Specifies where the body of the response is downloaded. Pass a string to + open a local file on disk and save the output to the file. Pass an fopen + resource to save the output to a PHP stream resource. Pass a + ``GuzzleHttp\Stream\StreamInterface`` to save the output to a Guzzle + StreamInterface. Omitting this option will typically save the body of a + response to a PHP temp stream. + + stream + (bool) Set to true to stream a response rather than download it all + up-front. This option will only be utilized when the corresponding + handler supports it. + + timeout + (float) Float describing the timeout of the request in seconds. Use 0 to + wait indefinitely (the default behavior). + + verify + (bool, string) Describes the SSL certificate verification behavior of a + request. Set to true to enable SSL certificate verification using the + system CA bundle when available (the default). Set to false to disable + certificate verification (this is insecure!). Set to a string to provide + the path to a CA bundle on disk to enable verification using a custom + certificate. + + version + (string) HTTP protocol version to use with the request. + +Server Specific Options +~~~~~~~~~~~~~~~~~~~~~~~ + +The following options are only used in ring server handlers. + +server_port + (integer) The port on which the request is being handled. This is only + used with ring servers, and is required. + +server_name + (string) The resolved server name, or the server IP address. Required when + using a Ring server. + +remote_addr + (string) The IP address of the client or the last proxy that sent the + request. Required when using a Ring server. + +Responses +--------- + +A response is an array-like object that implements +``GuzzleHttp\Ring\Future\FutureArrayInterface``. Responses contain the +following key value pairs: + +body + (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) + The body of the response, if present. Can be a string, resource returned + from fopen, an ``Iterator`` that yields chunks of data, an object that + implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. + +effective_url + (string) The URL that returned the resulting response. + +error + (``\Exception``) Contains an exception describing any errors that were + encountered during the transfer. + +headers + (Required, array) Associative array of headers. Each key represents the + header name. Each value contains an array of strings where each entry of + the array is a header line. The headers array MAY be an empty array in the + event an error occurred before a response was received. + +reason + (string) Optional reason phrase. This option should be provided when the + reason phrase does not match the typical reason phrase associated with the + ``status`` code. See `RFC 7231 `_ + for a list of HTTP reason phrases mapped to status codes. + +status + (Required, integer) The HTTP status code. The status code MAY be set to + ``null`` in the event an error occurred before a response was received + (e.g., a networking error). + +transfer_stats + (array) Provides an associative array of arbitrary transfer statistics if + provided by the underlying handler. + +version + (string) HTTP protocol version. Defaults to ``1.1``. + +Middleware +---------- + +Ring middleware augments the functionality of handlers by invoking them in the +process of generating responses. Middleware is typically implemented as a +higher-order function that takes one or more handlers as arguments followed by +an optional associative array of options as the last argument, returning a new +handler with the desired compound behavior. + +Here's an example of a middleware that adds a Content-Type header to each +request. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + use GuzzleHttp\Ring\Core; + + $contentTypeHandler = function(callable $handler, $contentType) { + return function (array $request) use ($handler, $contentType) { + return $handler(Core::setHeader('Content-Type', $contentType)); + }; + }; + + $baseHandler = new CurlHandler(); + $wrappedHandler = $contentTypeHandler($baseHandler, 'text/html'); + $response = $wrappedHandler([/** request hash **/]); diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/testing.rst b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/testing.rst new file mode 100644 index 00000000..9df2562e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/docs/testing.rst @@ -0,0 +1,74 @@ +======= +Testing +======= + +RingPHP tests client handlers using `PHPUnit `_ and a +built-in node.js web server. + +Running Tests +------------- + +First, install the dependencies using `Composer `_. + + composer.phar install + +Next, run the unit tests using ``Make``. + + make test + +The tests are also run on Travis-CI on each commit: https://travis-ci.org/guzzle/guzzle-ring + +Test Server +----------- + +Testing client handlers usually involves actually sending HTTP requests. +RingPHP provides a node.js web server that returns canned responses and +keep a list of the requests that have been received. The server can then +be queried to get a list of the requests that were sent by the client so that +you can ensure that the client serialized and transferred requests as intended. + +The server keeps a list of queued responses and returns responses that are +popped off of the queue as HTTP requests are received. When there are not +more responses to serve, the server returns a 500 error response. + +The test server uses the ``GuzzleHttp\Tests\Ring\Client\Server`` class to +control the server. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\StreamHandler; + use GuzzleHttp\Tests\Ring\Client\Server; + + // First return a 200 followed by a 404 response. + Server::enqueue([ + ['status' => 200], + ['status' => 404] + ]); + + $handler = new StreamHandler(); + + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/' + ]); + + assert(200 == $response['status']); + + $response = $handler([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/' + ]); + + assert(404 == $response['status']); + +After requests have been sent, you can get a list of the requests as they +were sent over the wire to ensure they were sent correctly. + +.. code-block:: php + + $received = Server::received(); + + assert('GET' == $received[0]['http_method']); + assert('HEAD' == $received[1]['http_method']); diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/phpunit.xml.dist b/lib/Ebanx/vendor/guzzlehttp/ringphp/phpunit.xml.dist new file mode 100644 index 00000000..1d192902 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + tests + + + + + src + + + diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php new file mode 100644 index 00000000..2acf92eb --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php @@ -0,0 +1,74 @@ +getDefaultOptions($request, $headers); + $this->applyMethod($request, $options); + + if (isset($request['client'])) { + $this->applyHandlerOptions($request, $options); + } + + $this->applyHeaders($request, $options); + unset($options['_headers']); + + // Add handler options from the request's configuration options + if (isset($request['client']['curl'])) { + $options = $this->applyCustomCurlOptions( + $request['client']['curl'], + $options + ); + } + + if (!$handle) { + $handle = curl_init(); + } + + $body = $this->getOutputBody($request, $options); + curl_setopt_array($handle, $options); + + return [$handle, &$headers, $body]; + } + + /** + * Creates a response hash from a cURL result. + * + * @param callable $handler Handler that was used. + * @param array $request Request that sent. + * @param array $response Response hash to update. + * @param array $headers Headers received during transfer. + * @param resource $body Body fopen response. + * + * @return array + */ + public static function createResponse( + callable $handler, + array $request, + array $response, + array $headers, + $body + ) { + if (isset($response['transfer_stats']['url'])) { + $response['effective_url'] = $response['transfer_stats']['url']; + } + + if (!empty($headers)) { + $startLine = explode(' ', array_shift($headers), 3); + $headerList = Core::headersFromLines($headers); + $response['headers'] = $headerList; + $response['version'] = isset($startLine[0]) ? substr($startLine[0], 5) : null; + $response['status'] = isset($startLine[1]) ? (int) $startLine[1] : null; + $response['reason'] = isset($startLine[2]) ? $startLine[2] : null; + $response['body'] = $body; + Core::rewindBody($response); + } + + return !empty($response['curl']['errno']) || !isset($response['status']) + ? self::createErrorResponse($handler, $request, $response) + : $response; + } + + private static function createErrorResponse( + callable $handler, + array $request, + array $response + ) { + static $connectionErrors = [ + CURLE_OPERATION_TIMEOUTED => true, + CURLE_COULDNT_RESOLVE_HOST => true, + CURLE_COULDNT_CONNECT => true, + CURLE_SSL_CONNECT_ERROR => true, + CURLE_GOT_NOTHING => true, + ]; + + // Retry when nothing is present or when curl failed to rewind. + if (!isset($response['err_message']) + && (empty($response['curl']['errno']) + || $response['curl']['errno'] == 65) + ) { + return self::retryFailedRewind($handler, $request, $response); + } + + $message = isset($response['err_message']) + ? $response['err_message'] + : sprintf('cURL error %s: %s', + $response['curl']['errno'], + isset($response['curl']['error']) + ? $response['curl']['error'] + : 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html'); + + $error = isset($response['curl']['errno']) + && isset($connectionErrors[$response['curl']['errno']]) + ? new ConnectException($message) + : new RingException($message); + + return $response + [ + 'status' => null, + 'reason' => null, + 'body' => null, + 'headers' => [], + 'error' => $error, + ]; + } + + private function getOutputBody(array $request, array &$options) + { + // Determine where the body of the response (if any) will be streamed. + if (isset($options[CURLOPT_WRITEFUNCTION])) { + return $request['client']['save_to']; + } + + if (isset($options[CURLOPT_FILE])) { + return $options[CURLOPT_FILE]; + } + + if ($request['http_method'] != 'HEAD') { + // Create a default body if one was not provided + return $options[CURLOPT_FILE] = fopen('php://temp', 'w+'); + } + + return null; + } + + private function getDefaultOptions(array $request, array &$headers) + { + $url = Core::url($request); + $startingResponse = false; + + $options = [ + '_headers' => $request['headers'], + CURLOPT_CUSTOMREQUEST => $request['http_method'], + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers, &$startingResponse) { + $value = trim($h); + if ($value === '') { + $startingResponse = true; + } elseif ($startingResponse) { + $startingResponse = false; + $headers = [$value]; + } else { + $headers[] = $value; + } + return strlen($h); + }, + ]; + + if (isset($request['version'])) { + if ($request['version'] == 2.0) { + $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + } else if ($request['version'] == 1.1) { + $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + } else { + $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + } + } + + if (defined('CURLOPT_PROTOCOLS')) { + $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + return $options; + } + + private function applyMethod(array $request, array &$options) + { + if (isset($request['body'])) { + $this->applyBody($request, $options); + return; + } + + switch ($request['http_method']) { + case 'PUT': + case 'POST': + // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + if (!Core::hasHeader($request, 'Content-Length')) { + $options[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; + } + break; + case 'HEAD': + $options[CURLOPT_NOBODY] = true; + unset( + $options[CURLOPT_WRITEFUNCTION], + $options[CURLOPT_READFUNCTION], + $options[CURLOPT_FILE], + $options[CURLOPT_INFILE] + ); + } + } + + private function applyBody(array $request, array &$options) + { + $contentLength = Core::firstHeader($request, 'Content-Length'); + $size = $contentLength !== null ? (int) $contentLength : null; + + // Send the body as a string if the size is less than 1MB OR if the + // [client][curl][body_as_string] request value is set. + if (($size !== null && $size < 1000000) || + isset($request['client']['curl']['body_as_string']) || + is_string($request['body']) + ) { + $options[CURLOPT_POSTFIELDS] = Core::body($request); + // Don't duplicate the Content-Length header + $this->removeHeader('Content-Length', $options); + $this->removeHeader('Transfer-Encoding', $options); + } else { + $options[CURLOPT_UPLOAD] = true; + if ($size !== null) { + // Let cURL handle setting the Content-Length header + $options[CURLOPT_INFILESIZE] = $size; + $this->removeHeader('Content-Length', $options); + } + $this->addStreamingBody($request, $options); + } + + // If the Expect header is not present, prevent curl from adding it + if (!Core::hasHeader($request, 'Expect')) { + $options[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + + // cURL sometimes adds a content-type by default. Prevent this. + if (!Core::hasHeader($request, 'Content-Type')) { + $options[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } + + private function addStreamingBody(array $request, array &$options) + { + $body = $request['body']; + + if ($body instanceof StreamInterface) { + $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { + return (string) $body->read($length); + }; + if (!isset($options[CURLOPT_INFILESIZE])) { + if ($size = $body->getSize()) { + $options[CURLOPT_INFILESIZE] = $size; + } + } + } elseif (is_resource($body)) { + $options[CURLOPT_INFILE] = $body; + } elseif ($body instanceof \Iterator) { + $buf = ''; + $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, &$buf) { + if ($body->valid()) { + $buf .= $body->current(); + $body->next(); + } + $result = (string) substr($buf, 0, $length); + $buf = substr($buf, $length); + return $result; + }; + } else { + throw new \InvalidArgumentException('Invalid request body provided'); + } + } + + private function applyHeaders(array $request, array &$options) + { + foreach ($options['_headers'] as $name => $values) { + foreach ($values as $value) { + $options[CURLOPT_HTTPHEADER][] = "$name: $value"; + } + } + + // Remove the Accept header if one was not set + if (!Core::hasHeader($request, 'Accept')) { + $options[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + } + + /** + * Takes an array of curl options specified in the 'curl' option of a + * request's configuration array and maps them to CURLOPT_* options. + * + * This method is only called when a request has a 'curl' config setting. + * + * @param array $config Configuration array of custom curl option + * @param array $options Array of existing curl options + * + * @return array Returns a new array of curl options + */ + private function applyCustomCurlOptions(array $config, array $options) + { + $curlOptions = []; + foreach ($config as $key => $value) { + if (is_int($key)) { + $curlOptions[$key] = $value; + } + } + + return $curlOptions + $options; + } + + /** + * Remove a header from the options array. + * + * @param string $name Case-insensitive header to remove + * @param array $options Array of options to modify + */ + private function removeHeader($name, array &$options) + { + foreach (array_keys($options['_headers']) as $key) { + if (!strcasecmp($key, $name)) { + unset($options['_headers'][$key]); + return; + } + } + } + + /** + * Applies an array of request client options to a the options array. + * + * This method uses a large switch rather than double-dispatch to save on + * high overhead of calling functions in PHP. + */ + private function applyHandlerOptions(array $request, array &$options) + { + foreach ($request['client'] as $key => $value) { + switch ($key) { + // Violating PSR-4 to provide more room. + case 'verify': + + if ($value === false) { + unset($options[CURLOPT_CAINFO]); + $options[CURLOPT_SSL_VERIFYHOST] = 0; + $options[CURLOPT_SSL_VERIFYPEER] = false; + continue; + } + + $options[CURLOPT_SSL_VERIFYHOST] = 2; + $options[CURLOPT_SSL_VERIFYPEER] = true; + + if (is_string($value)) { + $options[CURLOPT_CAINFO] = $value; + if (!file_exists($value)) { + throw new \InvalidArgumentException( + "SSL CA bundle not found: $value" + ); + } + } + break; + + case 'decode_content': + + if ($value === false) { + continue; + } + + $accept = Core::firstHeader($request, 'Accept-Encoding'); + if ($accept) { + $options[CURLOPT_ENCODING] = $accept; + } else { + $options[CURLOPT_ENCODING] = ''; + // Don't let curl send the header over the wire + $options[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; + } + break; + + case 'save_to': + + if (is_string($value)) { + if (!is_dir(dirname($value))) { + throw new \RuntimeException(sprintf( + 'Directory %s does not exist for save_to value of %s', + dirname($value), + $value + )); + } + $value = new LazyOpenStream($value, 'w+'); + } + + if ($value instanceof StreamInterface) { + $options[CURLOPT_WRITEFUNCTION] = + function ($ch, $write) use ($value) { + return $value->write($write); + }; + } elseif (is_resource($value)) { + $options[CURLOPT_FILE] = $value; + } else { + throw new \InvalidArgumentException('save_to must be a ' + . 'GuzzleHttp\Stream\StreamInterface or resource'); + } + break; + + case 'timeout': + + if (defined('CURLOPT_TIMEOUT_MS')) { + $options[CURLOPT_TIMEOUT_MS] = $value * 1000; + } else { + $options[CURLOPT_TIMEOUT] = $value; + } + break; + + case 'connect_timeout': + + if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { + $options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000; + } else { + $options[CURLOPT_CONNECTTIMEOUT] = $value; + } + break; + + case 'proxy': + + if (!is_array($value)) { + $options[CURLOPT_PROXY] = $value; + } elseif (isset($request['scheme'])) { + $scheme = $request['scheme']; + if (isset($value[$scheme])) { + $options[CURLOPT_PROXY] = $value[$scheme]; + } + } + break; + + case 'cert': + + if (is_array($value)) { + $options[CURLOPT_SSLCERTPASSWD] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \InvalidArgumentException( + "SSL certificate not found: {$value}" + ); + } + + $options[CURLOPT_SSLCERT] = $value; + break; + + case 'ssl_key': + + if (is_array($value)) { + $options[CURLOPT_SSLKEYPASSWD] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \InvalidArgumentException( + "SSL private key not found: {$value}" + ); + } + + $options[CURLOPT_SSLKEY] = $value; + break; + + case 'progress': + + if (!is_callable($value)) { + throw new \InvalidArgumentException( + 'progress client option must be callable' + ); + } + + $options[CURLOPT_NOPROGRESS] = false; + $options[CURLOPT_PROGRESSFUNCTION] = + function () use ($value) { + $args = func_get_args(); + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + call_user_func_array($value, $args); + }; + break; + + case 'debug': + + if ($value) { + $options[CURLOPT_STDERR] = Core::getDebugResource($value); + $options[CURLOPT_VERBOSE] = true; + } + break; + } + } + } + + /** + * This function ensures that a response was set on a transaction. If one + * was not set, then the request is retried if possible. This error + * typically means you are sending a payload, curl encountered a + * "Connection died, retrying a fresh connect" error, tried to rewind the + * stream, and then encountered a "necessary data rewind wasn't possible" + * error, causing the request to be sent through curl_multi_info_read() + * without an error status. + */ + private static function retryFailedRewind( + callable $handler, + array $request, + array $response + ) { + // If there is no body, then there is some other kind of issue. This + // is weird and should probably never happen. + if (!isset($request['body'])) { + $response['err_message'] = 'No response was received for a request ' + . 'with no body. This could mean that you are saturating your ' + . 'network.'; + return self::createErrorResponse($handler, $request, $response); + } + + if (!Core::rewindBody($request)) { + $response['err_message'] = 'The connection unexpectedly failed ' + . 'without providing an error. The request would have been ' + . 'retried, but attempting to rewind the request body failed.'; + return self::createErrorResponse($handler, $request, $response); + } + + // Retry no more than 3 times before giving up. + if (!isset($request['curl']['retries'])) { + $request['curl']['retries'] = 1; + } elseif ($request['curl']['retries'] == 2) { + $response['err_message'] = 'The cURL request was retried 3 times ' + . 'and did no succeed. cURL was unable to rewind the body of ' + . 'the request and subsequent retries resulted in the same ' + . 'error. Turn on the debug option to see what went wrong. ' + . 'See https://bugs.php.net/bug.php?id=47204 for more information.'; + return self::createErrorResponse($handler, $request, $response); + } else { + $request['curl']['retries']++; + } + + return $handler($request); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php new file mode 100644 index 00000000..e00aa4ea --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php @@ -0,0 +1,135 @@ +handles = $this->ownedHandles = []; + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] + : new CurlFactory(); + $this->maxHandles = isset($options['max_handles']) + ? $options['max_handles'] + : 5; + } + + public function __destruct() + { + foreach ($this->handles as $handle) { + if (is_resource($handle)) { + curl_close($handle); + } + } + } + + /** + * @param array $request + * + * @return CompletedFutureArray + */ + public function __invoke(array $request) + { + return new CompletedFutureArray( + $this->_invokeAsArray($request) + ); + } + + /** + * @internal + * + * @param array $request + * + * @return array + */ + public function _invokeAsArray(array $request) + { + $factory = $this->factory; + + // Ensure headers are by reference. They're updated elsewhere. + $result = $factory($request, $this->checkoutEasyHandle()); + $h = $result[0]; + $hd =& $result[1]; + $bd = $result[2]; + Core::doSleep($request); + curl_exec($h); + $response = ['transfer_stats' => curl_getinfo($h)]; + $response['curl']['error'] = curl_error($h); + $response['curl']['errno'] = curl_errno($h); + $response['transfer_stats'] = array_merge($response['transfer_stats'], $response['curl']); + $this->releaseEasyHandle($h); + + return CurlFactory::createResponse([$this, '_invokeAsArray'], $request, $response, $hd, $bd); + } + + private function checkoutEasyHandle() + { + // Find an unused handle in the cache + if (false !== ($key = array_search(false, $this->ownedHandles, true))) { + $this->ownedHandles[$key] = true; + return $this->handles[$key]; + } + + // Add a new handle + $handle = curl_init(); + $id = (int) $handle; + $this->handles[$id] = $handle; + $this->ownedHandles[$id] = true; + + return $handle; + } + + private function releaseEasyHandle($handle) + { + $id = (int) $handle; + if (count($this->ownedHandles) > $this->maxHandles) { + curl_close($this->handles[$id]); + unset($this->handles[$id], $this->ownedHandles[$id]); + } else { + // curl_reset doesn't clear these out for some reason + static $unsetValues = [ + CURLOPT_HEADERFUNCTION => null, + CURLOPT_WRITEFUNCTION => null, + CURLOPT_READFUNCTION => null, + CURLOPT_PROGRESSFUNCTION => null, + ]; + curl_setopt_array($handle, $unsetValues); + curl_reset($handle); + $this->ownedHandles[$id] = false; + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php new file mode 100644 index 00000000..f84cf199 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php @@ -0,0 +1,248 @@ +_mh = $options['mh']; + } + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] : new CurlFactory(); + $this->selectTimeout = isset($options['select_timeout']) + ? $options['select_timeout'] : 1; + $this->maxHandles = isset($options['max_handles']) + ? $options['max_handles'] : 100; + } + + public function __get($name) + { + if ($name === '_mh') { + return $this->_mh = curl_multi_init(); + } + + throw new \BadMethodCallException(); + } + + public function __destruct() + { + // Finish any open connections before terminating the script. + if ($this->handles) { + $this->execute(); + } + + if (isset($this->_mh)) { + curl_multi_close($this->_mh); + unset($this->_mh); + } + } + + public function __invoke(array $request) + { + $factory = $this->factory; + $result = $factory($request); + $entry = [ + 'request' => $request, + 'response' => [], + 'handle' => $result[0], + 'headers' => &$result[1], + 'body' => $result[2], + 'deferred' => new Deferred(), + ]; + + $id = (int) $result[0]; + + $future = new FutureArray( + $entry['deferred']->promise(), + [$this, 'execute'], + function () use ($id) { + return $this->cancel($id); + } + ); + + $this->addRequest($entry); + + // Transfer outstanding requests if there are too many open handles. + if (count($this->handles) >= $this->maxHandles) { + $this->execute(); + } + + return $future; + } + + /** + * Runs until all outstanding connections have completed. + */ + public function execute() + { + do { + + if ($this->active && + curl_multi_select($this->_mh, $this->selectTimeout) === -1 + ) { + // Perform a usleep if a select returns -1. + // See: https://bugs.php.net/bug.php?id=61141 + usleep(250); + } + + // Add any delayed futures if needed. + if ($this->delays) { + $this->addDelays(); + } + + do { + $mrc = curl_multi_exec($this->_mh, $this->active); + } while ($mrc === CURLM_CALL_MULTI_PERFORM); + + $this->processMessages(); + + // If there are delays but no transfers, then sleep for a bit. + if (!$this->active && $this->delays) { + usleep(500); + } + + } while ($this->active || $this->handles); + } + + private function addRequest(array &$entry) + { + $id = (int) $entry['handle']; + $this->handles[$id] = $entry; + + // If the request is a delay, then add the reques to the curl multi + // pool only after the specified delay. + if (isset($entry['request']['client']['delay'])) { + $this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000); + } elseif (empty($entry['request']['future'])) { + curl_multi_add_handle($this->_mh, $entry['handle']); + } else { + curl_multi_add_handle($this->_mh, $entry['handle']); + // "lazy" futures are only sent once the pool has many requests. + if ($entry['request']['future'] !== 'lazy') { + do { + $mrc = curl_multi_exec($this->_mh, $this->active); + } while ($mrc === CURLM_CALL_MULTI_PERFORM); + $this->processMessages(); + } + } + } + + private function removeProcessed($id) + { + if (isset($this->handles[$id])) { + curl_multi_remove_handle( + $this->_mh, + $this->handles[$id]['handle'] + ); + curl_close($this->handles[$id]['handle']); + unset($this->handles[$id], $this->delays[$id]); + } + } + + /** + * Cancels a handle from sending and removes references to it. + * + * @param int $id Handle ID to cancel and remove. + * + * @return bool True on success, false on failure. + */ + private function cancel($id) + { + // Cannot cancel if it has been processed. + if (!isset($this->handles[$id])) { + return false; + } + + $handle = $this->handles[$id]['handle']; + unset($this->delays[$id], $this->handles[$id]); + curl_multi_remove_handle($this->_mh, $handle); + curl_close($handle); + + return true; + } + + private function addDelays() + { + $currentTime = microtime(true); + + foreach ($this->delays as $id => $delay) { + if ($currentTime >= $delay) { + unset($this->delays[$id]); + curl_multi_add_handle( + $this->_mh, + $this->handles[$id]['handle'] + ); + } + } + } + + private function processMessages() + { + while ($done = curl_multi_info_read($this->_mh)) { + $id = (int) $done['handle']; + + if (!isset($this->handles[$id])) { + // Probably was cancelled. + continue; + } + + $entry = $this->handles[$id]; + $entry['response']['transfer_stats'] = curl_getinfo($done['handle']); + + if ($done['result'] !== CURLM_OK) { + $entry['response']['curl']['errno'] = $done['result']; + $entry['response']['curl']['error'] = curl_error($done['handle']); + } + + $result = CurlFactory::createResponse( + $this, + $entry['request'], + $entry['response'], + $entry['headers'], + $entry['body'] + ); + + $this->removeProcessed($id); + $entry['deferred']->resolve($result); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/Middleware.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/Middleware.php new file mode 100644 index 00000000..6fa7318a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/Middleware.php @@ -0,0 +1,58 @@ +result = $result; + } + + public function __invoke(array $request) + { + Core::doSleep($request); + $response = is_callable($this->result) + ? call_user_func($this->result, $request) + : $this->result; + + if (is_array($response)) { + $response = new CompletedFutureArray($response + [ + 'status' => null, + 'body' => null, + 'headers' => [], + 'reason' => null, + 'effective_url' => null, + ]); + } elseif (!$response instanceof FutureArrayInterface) { + throw new \InvalidArgumentException( + 'Response must be an array or FutureArrayInterface. Found ' + . Core::describeType($request) + ); + } + + return $response; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php new file mode 100644 index 00000000..4bacec13 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php @@ -0,0 +1,414 @@ +options = $options; + } + + public function __invoke(array $request) + { + $url = Core::url($request); + Core::doSleep($request); + + try { + // Does not support the expect header. + $request = Core::removeHeader($request, 'Expect'); + $stream = $this->createStream($url, $request); + return $this->createResponse($request, $url, $stream); + } catch (RingException $e) { + return $this->createErrorResponse($url, $e); + } + } + + private function createResponse(array $request, $url, $stream) + { + $hdrs = $this->lastHeaders; + $this->lastHeaders = null; + $parts = explode(' ', array_shift($hdrs), 3); + $response = [ + 'version' => substr($parts[0], 5), + 'status' => $parts[1], + 'reason' => isset($parts[2]) ? $parts[2] : null, + 'headers' => Core::headersFromLines($hdrs), + 'effective_url' => $url, + ]; + + $stream = $this->checkDecode($request, $response, $stream); + + // If not streaming, then drain the response into a stream. + if (empty($request['client']['stream'])) { + $dest = isset($request['client']['save_to']) + ? $request['client']['save_to'] + : fopen('php://temp', 'r+'); + $stream = $this->drain($stream, $dest); + } + + $response['body'] = $stream; + + return new CompletedFutureArray($response); + } + + private function checkDecode(array $request, array $response, $stream) + { + // Automatically decode responses when instructed. + if (!empty($request['client']['decode_content'])) { + switch (Core::firstHeader($response, 'Content-Encoding', true)) { + case 'gzip': + case 'deflate': + $stream = new InflateStream(Stream::factory($stream)); + break; + } + } + + return $stream; + } + + /** + * Drains the stream into the "save_to" client option. + * + * @param resource $stream + * @param string|resource|StreamInterface $dest + * + * @return Stream + * @throws \RuntimeException when the save_to option is invalid. + */ + private function drain($stream, $dest) + { + if (is_resource($stream)) { + if (!is_resource($dest)) { + $stream = Stream::factory($stream); + } else { + stream_copy_to_stream($stream, $dest); + fclose($stream); + rewind($dest); + return $dest; + } + } + + // Stream the response into the destination stream + $dest = is_string($dest) + ? new Stream(Utils::open($dest, 'r+')) + : Stream::factory($dest); + + Utils::copyToStream($stream, $dest); + $dest->seek(0); + $stream->close(); + + return $dest; + } + + /** + * Creates an error response for the given stream. + * + * @param string $url + * @param RingException $e + * + * @return array + */ + private function createErrorResponse($url, RingException $e) + { + // Determine if the error was a networking error. + $message = $e->getMessage(); + + // This list can probably get more comprehensive. + if (strpos($message, 'getaddrinfo') // DNS lookup failed + || strpos($message, 'Connection refused') + ) { + $e = new ConnectException($e->getMessage(), 0, $e); + } + + return new CompletedFutureArray([ + 'status' => null, + 'body' => null, + 'headers' => [], + 'effective_url' => $url, + 'error' => $e + ]); + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Callable that returns stream resource + * + * @return resource + * @throws \RuntimeException on error + */ + private function createResource(callable $callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = [ + 'message' => $msg, + 'file' => $file, + 'line' => $line + ]; + return true; + }); + + $resource = $callback(); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource: '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new RingException(trim($message)); + } + + return $resource; + } + + private function createStream($url, array $request) + { + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + // HTTP/1.1 streams using the PHP stream wrapper require a + // Connection: close header + if ((!isset($request['version']) || $request['version'] == '1.1') + && !Core::hasHeader($request, 'Connection') + ) { + $request['headers']['Connection'] = ['close']; + } + + // Ensure SSL is verified by default + if (!isset($request['client']['verify'])) { + $request['client']['verify'] = true; + } + + $params = []; + $options = $this->getDefaultOptions($request); + + if (isset($request['client'])) { + foreach ($request['client'] as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $options, $value, $params); + } + } + } + + return $this->createStreamResource( + $url, + $request, + $options, + $this->createContext($request, $options, $params) + ); + } + + private function getDefaultOptions(array $request) + { + $headers = ""; + foreach ($request['headers'] as $name => $value) { + foreach ((array) $value as $val) { + $headers .= "$name: $val\r\n"; + } + } + + $context = [ + 'http' => [ + 'method' => $request['http_method'], + 'header' => $headers, + 'protocol_version' => isset($request['version']) ? $request['version'] : 1.1, + 'ignore_errors' => true, + 'follow_location' => 0, + ], + ]; + + $body = Core::body($request); + if (isset($body)) { + $context['http']['content'] = $body; + // Prevent the HTTP handler from adding a Content-Type header. + if (!Core::hasHeader($request, 'Content-Type')) { + $context['http']['header'] .= "Content-Type:\r\n"; + } + } + + $context['http']['header'] = rtrim($context['http']['header']); + + return $context; + } + + private function add_proxy(array $request, &$options, $value, &$params) + { + if (!is_array($value)) { + $options['http']['proxy'] = $value; + } else { + $scheme = isset($request['scheme']) ? $request['scheme'] : 'http'; + if (isset($value[$scheme])) { + $options['http']['proxy'] = $value[$scheme]; + } + } + } + + private function add_timeout(array $request, &$options, $value, &$params) + { + $options['http']['timeout'] = $value; + } + + private function add_verify(array $request, &$options, $value, &$params) + { + if ($value === true) { + // PHP 5.6 or greater will find the system cert by default. When + // < 5.6, use the Guzzle bundled cacert. + if (PHP_VERSION_ID < 50600) { + $options['ssl']['cafile'] = ClientUtils::getDefaultCaBundle(); + } + } elseif (is_string($value)) { + $options['ssl']['cafile'] = $value; + if (!file_exists($value)) { + throw new RingException("SSL CA bundle not found: $value"); + } + } elseif ($value === false) { + $options['ssl']['verify_peer'] = false; + $options['ssl']['allow_self_signed'] = true; + return; + } else { + throw new RingException('Invalid verify request option'); + } + + $options['ssl']['verify_peer'] = true; + $options['ssl']['allow_self_signed'] = false; + } + + private function add_cert(array $request, &$options, $value, &$params) + { + if (is_array($value)) { + $options['ssl']['passphrase'] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new RingException("SSL certificate not found: {$value}"); + } + + $options['ssl']['local_cert'] = $value; + } + + private function add_progress(array $request, &$options, $value, &$params) + { + $fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) { + if ($code == STREAM_NOTIFY_PROGRESS) { + $value($total, $transferred, null, null); + } + }; + + // Wrap the existing function if needed. + $params['notification'] = isset($params['notification']) + ? Core::callArray([$params['notification'], $fn]) + : $fn; + } + + private function add_debug(array $request, &$options, $value, &$params) + { + if ($value === false) { + return; + } + + static $map = [ + STREAM_NOTIFY_CONNECT => 'CONNECT', + STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', + STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', + STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', + STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', + STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', + STREAM_NOTIFY_PROGRESS => 'PROGRESS', + STREAM_NOTIFY_FAILURE => 'FAILURE', + STREAM_NOTIFY_COMPLETED => 'COMPLETED', + STREAM_NOTIFY_RESOLVE => 'RESOLVE', + ]; + + static $args = ['severity', 'message', 'message_code', + 'bytes_transferred', 'bytes_max']; + + $value = Core::getDebugResource($value); + $ident = $request['http_method'] . ' ' . Core::url($request); + $fn = function () use ($ident, $value, $map, $args) { + $passed = func_get_args(); + $code = array_shift($passed); + fprintf($value, '<%s> [%s] ', $ident, $map[$code]); + foreach (array_filter($passed) as $i => $v) { + fwrite($value, $args[$i] . ': "' . $v . '" '); + } + fwrite($value, "\n"); + }; + + // Wrap the existing function if needed. + $params['notification'] = isset($params['notification']) + ? Core::callArray([$params['notification'], $fn]) + : $fn; + } + + private function applyCustomOptions(array $request, array &$options) + { + if (!isset($request['client']['stream_context'])) { + return; + } + + if (!is_array($request['client']['stream_context'])) { + throw new RingException('stream_context must be an array'); + } + + $options = array_replace_recursive( + $options, + $request['client']['stream_context'] + ); + } + + private function createContext(array $request, array $options, array $params) + { + $this->applyCustomOptions($request, $options); + return $this->createResource( + function () use ($request, $options, $params) { + return stream_context_create($options, $params); + }, + $request, + $options + ); + } + + private function createStreamResource( + $url, + array $request, + array $options, + $context + ) { + return $this->createResource( + function () use ($url, $context) { + if (false === strpos($url, 'http')) { + trigger_error("URL is invalid: {$url}", E_USER_WARNING); + return null; + } + $resource = fopen($url, 'r', null, $context); + $this->lastHeaders = $http_response_header; + return $resource; + }, + $request, + $options + ); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Core.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Core.php new file mode 100644 index 00000000..dd7d1a0c --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Core.php @@ -0,0 +1,364 @@ + $value) { + if (!strcasecmp($name, $header)) { + $result = array_merge($result, $value); + } + } + } + + return $result; + } + + /** + * Gets a header value from a message as a string or null + * + * This method searches through the "headers" key of a message for a header + * using a case-insensitive search. The lines of the header are imploded + * using commas into a single string return value. + * + * @param array $message Request or response hash. + * @param string $header Header to retrieve + * + * @return string|null Returns the header string if found, or null if not. + */ + public static function header($message, $header) + { + $match = self::headerLines($message, $header); + return $match ? implode(', ', $match) : null; + } + + /** + * Returns the first header value from a message as a string or null. If + * a header line contains multiple values separated by a comma, then this + * function will return the first value in the list. + * + * @param array $message Request or response hash. + * @param string $header Header to retrieve + * + * @return string|null Returns the value as a string if found. + */ + public static function firstHeader($message, $header) + { + if (!empty($message['headers'])) { + foreach ($message['headers'] as $name => $value) { + if (!strcasecmp($name, $header)) { + // Return the match itself if it is a single value. + $pos = strpos($value[0], ','); + return $pos ? substr($value[0], 0, $pos) : $value[0]; + } + } + } + + return null; + } + + /** + * Returns true if a message has the provided case-insensitive header. + * + * @param array $message Request or response hash. + * @param string $header Header to check + * + * @return bool + */ + public static function hasHeader($message, $header) + { + if (!empty($message['headers'])) { + foreach ($message['headers'] as $name => $value) { + if (!strcasecmp($name, $header)) { + return true; + } + } + } + + return false; + } + + /** + * Parses an array of header lines into an associative array of headers. + * + * @param array $lines Header lines array of strings in the following + * format: "Name: Value" + * @return array + */ + public static function headersFromLines($lines) + { + $headers = []; + + foreach ($lines as $line) { + $parts = explode(':', $line, 2); + $headers[trim($parts[0])][] = isset($parts[1]) + ? trim($parts[1]) + : null; + } + + return $headers; + } + + /** + * Removes a header from a message using a case-insensitive comparison. + * + * @param array $message Message that contains 'headers' + * @param string $header Header to remove + * + * @return array + */ + public static function removeHeader(array $message, $header) + { + if (isset($message['headers'])) { + foreach (array_keys($message['headers']) as $key) { + if (!strcasecmp($header, $key)) { + unset($message['headers'][$key]); + } + } + } + + return $message; + } + + /** + * Replaces any existing case insensitive headers with the given value. + * + * @param array $message Message that contains 'headers' + * @param string $header Header to set. + * @param array $value Value to set. + * + * @return array + */ + public static function setHeader(array $message, $header, array $value) + { + $message = self::removeHeader($message, $header); + $message['headers'][$header] = $value; + + return $message; + } + + /** + * Creates a URL string from a request. + * + * If the "url" key is present on the request, it is returned, otherwise + * the url is built up based on the scheme, host, uri, and query_string + * request values. + * + * @param array $request Request to get the URL from + * + * @return string Returns the request URL as a string. + * @throws \InvalidArgumentException if no Host header is present. + */ + public static function url(array $request) + { + if (isset($request['url'])) { + return $request['url']; + } + + $uri = (isset($request['scheme']) + ? $request['scheme'] : 'http') . '://'; + + if ($host = self::header($request, 'host')) { + $uri .= $host; + } else { + throw new \InvalidArgumentException('No Host header was provided'); + } + + if (isset($request['uri'])) { + $uri .= $request['uri']; + } + + if (isset($request['query_string'])) { + $uri .= '?' . $request['query_string']; + } + + return $uri; + } + + /** + * Reads the body of a message into a string. + * + * @param array|FutureArrayInterface $message Array containing a "body" key + * + * @return null|string Returns the body as a string or null if not set. + * @throws \InvalidArgumentException if a request body is invalid. + */ + public static function body($message) + { + if (!isset($message['body'])) { + return null; + } + + if ($message['body'] instanceof StreamInterface) { + return (string) $message['body']; + } + + switch (gettype($message['body'])) { + case 'string': + return $message['body']; + case 'resource': + return stream_get_contents($message['body']); + case 'object': + if ($message['body'] instanceof \Iterator) { + return implode('', iterator_to_array($message['body'])); + } elseif (method_exists($message['body'], '__toString')) { + return (string) $message['body']; + } + default: + throw new \InvalidArgumentException('Invalid request body: ' + . self::describeType($message['body'])); + } + } + + /** + * Rewind the body of the provided message if possible. + * + * @param array $message Message that contains a 'body' field. + * + * @return bool Returns true on success, false on failure + */ + public static function rewindBody($message) + { + if ($message['body'] instanceof StreamInterface) { + return $message['body']->seek(0); + } + + if ($message['body'] instanceof \Generator) { + return false; + } + + if ($message['body'] instanceof \Iterator) { + $message['body']->rewind(); + return true; + } + + if (is_resource($message['body'])) { + return rewind($message['body']); + } + + return is_string($message['body']) + || (is_object($message['body']) + && method_exists($message['body'], '__toString')); + } + + /** + * Debug function used to describe the provided value type and class. + * + * @param mixed $input + * + * @return string Returns a string containing the type of the variable and + * if a class is provided, the class name. + */ + public static function describeType($input) + { + switch (gettype($input)) { + case 'object': + return 'object(' . get_class($input) . ')'; + case 'array': + return 'array(' . count($input) . ')'; + default: + ob_start(); + var_dump($input); + // normalize float vs double + return str_replace('double(', 'float(', rtrim(ob_get_clean())); + } + } + + /** + * Sleep for the specified amount of time specified in the request's + * ['client']['delay'] option if present. + * + * This function should only be used when a non-blocking sleep is not + * possible. + * + * @param array $request Request to sleep + */ + public static function doSleep(array $request) + { + if (isset($request['client']['delay'])) { + usleep($request['client']['delay'] * 1000); + } + } + + /** + * Returns a proxied future that modifies the dereferenced value of another + * future using a promise. + * + * @param FutureArrayInterface $future Future to wrap with a new future + * @param callable $onFulfilled Invoked when the future fulfilled + * @param callable $onRejected Invoked when the future rejected + * @param callable $onProgress Invoked when the future progresses + * + * @return FutureArray + */ + public static function proxy( + FutureArrayInterface $future, + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return new FutureArray( + $future->then($onFulfilled, $onRejected, $onProgress), + [$future, 'wait'], + [$future, 'cancel'] + ); + } + + /** + * Returns a debug stream based on the provided variable. + * + * @param mixed $value Optional value + * + * @return resource + */ + public static function getDebugResource($value = null) + { + if (is_resource($value)) { + return $value; + } elseif (defined('STDOUT')) { + return STDOUT; + } else { + return fopen('php://output', 'w'); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php new file mode 100644 index 00000000..95b353ac --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php @@ -0,0 +1,7 @@ +wrappedPromise = $promise; + $this->waitfn = $wait; + $this->cancelfn = $cancel; + } + + public function wait() + { + if (!$this->isRealized) { + $this->addShadow(); + if (!$this->isRealized && $this->waitfn) { + $this->invokeWait(); + } + if (!$this->isRealized) { + $this->error = new RingException('Waiting did not resolve future'); + } + } + + if ($this->error) { + throw $this->error; + } + + return $this->result; + } + + public function promise() + { + return $this->wrappedPromise; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return $this->wrappedPromise->then($onFulfilled, $onRejected, $onProgress); + } + + public function cancel() + { + if (!$this->isRealized) { + $cancelfn = $this->cancelfn; + $this->waitfn = $this->cancelfn = null; + $this->isRealized = true; + $this->error = new CancelledFutureAccessException(); + if ($cancelfn) { + $cancelfn($this); + } + } + } + + private function addShadow() + { + // Get the result and error when the promise is resolved. Note that + // calling this function might trigger the resolution immediately. + $this->wrappedPromise->then( + function ($value) { + $this->isRealized = true; + $this->result = $value; + $this->waitfn = $this->cancelfn = null; + }, + function ($error) { + $this->isRealized = true; + $this->error = $error; + $this->waitfn = $this->cancelfn = null; + } + ); + } + + private function invokeWait() + { + try { + $wait = $this->waitfn; + $this->waitfn = null; + $wait(); + } catch (\Exception $e) { + // Defer can throw to reject. + $this->error = $e; + $this->isRealized = true; + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php new file mode 100644 index 00000000..0a90c939 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php @@ -0,0 +1,43 @@ +result[$offset]); + } + + public function offsetGet($offset) + { + return $this->result[$offset]; + } + + public function offsetSet($offset, $value) + { + $this->result[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->result[$offset]); + } + + public function count() + { + return count($this->result); + } + + public function getIterator() + { + return new \ArrayIterator($this->result); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php new file mode 100644 index 00000000..0d25af72 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php @@ -0,0 +1,57 @@ +result = $result; + $this->error = $e; + } + + public function wait() + { + if ($this->error) { + throw $this->error; + } + + return $this->result; + } + + public function cancel() {} + + public function promise() + { + if (!$this->cachedPromise) { + $this->cachedPromise = $this->error + ? new RejectedPromise($this->error) + : new FulfilledPromise($this->result); + } + + return $this->cachedPromise; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return $this->promise()->then($onFulfilled, $onRejected, $onProgress); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php new file mode 100644 index 00000000..3d64c964 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php @@ -0,0 +1,40 @@ +_value[$offset]); + } + + public function offsetGet($offset) + { + return $this->_value[$offset]; + } + + public function offsetSet($offset, $value) + { + $this->_value[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->_value[$offset]); + } + + public function count() + { + return count($this->_value); + } + + public function getIterator() + { + return new \ArrayIterator($this->_value); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php new file mode 100644 index 00000000..58f5f736 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php @@ -0,0 +1,11 @@ +_value = $this->wait(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php new file mode 100644 index 00000000..ebde187c --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php @@ -0,0 +1,821 @@ + 200, + 'headers' => [ + 'Foo' => ['Bar'], + 'Baz' => ['bam'], + 'Content-Length' => [2], + ], + 'body' => 'hi', + ]]); + + $stream = Stream::factory(); + + $request = [ + 'http_method' => 'PUT', + 'headers' => [ + 'host' => [Server::$url], + 'Hi' => [' 123'], + ], + 'body' => 'testing', + 'client' => ['save_to' => $stream], + ]; + + $f = new CurlFactory(); + $result = $f($request); + $this->assertInternalType('array', $result); + $this->assertCount(3, $result); + $this->assertInternalType('resource', $result[0]); + $this->assertInternalType('array', $result[1]); + $this->assertSame($stream, $result[2]); + curl_close($result[0]); + + $this->assertEquals('PUT', $_SERVER['_curl'][CURLOPT_CUSTOMREQUEST]); + $this->assertEquals( + 'http://http://127.0.0.1:8125/', + $_SERVER['_curl'][CURLOPT_URL] + ); + // Sends via post fields when the request is small enough + $this->assertEquals('testing', $_SERVER['_curl'][CURLOPT_POSTFIELDS]); + $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_RETURNTRANSFER]); + $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_HEADER]); + $this->assertEquals(150, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT]); + $this->assertInstanceOf('Closure', $_SERVER['_curl'][CURLOPT_HEADERFUNCTION]); + + if (defined('CURLOPT_PROTOCOLS')) { + $this->assertEquals( + CURLPROTO_HTTP | CURLPROTO_HTTPS, + $_SERVER['_curl'][CURLOPT_PROTOCOLS] + ); + } + + $this->assertContains('Expect:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('Accept:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('Content-Type:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('Hi: 123', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('host: http://127.0.0.1:8125/', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + } + + public function testSendsHeadRequests() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + ]); + $response->wait(); + $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]); + $checks = [CURLOPT_WRITEFUNCTION, CURLOPT_READFUNCTION, CURLOPT_FILE, CURLOPT_INFILE]; + foreach ($checks as $check) { + $this->assertArrayNotHasKey($check, $_SERVER['_curl']); + } + $this->assertEquals('HEAD', Server::received()[0]['http_method']); + } + + public function testCanAddCustomCurlOptions() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]], + ]); + $this->assertEquals(10, $_SERVER['_curl'][CURLOPT_LOW_SPEED_LIMIT]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage SSL CA bundle not found: /does/not/exist + */ + public function testValidatesVerify() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => '/does/not/exist'], + ]); + } + + public function testCanSetVerifyToFile() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => __FILE__], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_CAINFO]); + $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + } + + public function testAddsVerifyAsTrue() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => true], + ]); + $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + $this->assertArrayNotHasKey(CURLOPT_CAINFO, $_SERVER['_curl']); + } + + public function testCanDisableVerify() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => false], + ]); + $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(false, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + } + + public function testAddsProxy() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['proxy' => 'http://bar.com'], + ]); + $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); + } + + public function testAddsViaScheme() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'scheme' => 'http', + 'headers' => ['host' => ['foo.com']], + 'client' => [ + 'proxy' => ['http' => 'http://bar.com', 'https' => 'https://t'], + ], + ]); + $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage SSL private key not found: /does/not/exist + */ + public function testValidatesSslKey() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['ssl_key' => '/does/not/exist'], + ]); + } + + public function testAddsSslKey() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['ssl_key' => __FILE__], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); + } + + public function testAddsSslKeyWithPassword() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['ssl_key' => [__FILE__, 'test']], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); + $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLKEYPASSWD]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage SSL certificate not found: /does/not/exist + */ + public function testValidatesCert() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['cert' => '/does/not/exist'], + ]); + } + + public function testAddsCert() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['cert' => __FILE__], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); + } + + public function testAddsCertWithPassword() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['cert' => [__FILE__, 'test']], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); + $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLCERTPASSWD]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage progress client option must be callable + */ + public function testValidatesProgress() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['progress' => 'foo'], + ]); + } + + public function testEmitsDebugInfoToStream() + { + $res = fopen('php://memory', 'r+'); + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + 'client' => ['debug' => $res], + ]); + $response->wait(); + rewind($res); + $output = str_replace("\r", '', stream_get_contents($res)); + $this->assertContains( + "> HEAD / HTTP/1.1\nhost: 127.0.0.1:8125\n\n", + $output + ); + $this->assertContains("< HTTP/1.1 200", $output); + fclose($res); + } + + public function testEmitsProgressToFunction() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $called = []; + $response = $a([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'progress' => function () use (&$called) { + $called[] = func_get_args(); + }, + ], + ]); + $response->wait(); + $this->assertNotEmpty($called); + foreach ($called as $call) { + $this->assertCount(4, $call); + } + } + + private function addDecodeResponse($withEncoding = true) + { + $content = gzencode('test'); + $response = [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => ['Content-Length' => [strlen($content)]], + 'body' => $content, + ]; + + if ($withEncoding) { + $response['headers']['Content-Encoding'] = ['gzip']; + } + + Server::flush(); + Server::enqueue([$response]); + + return $content; + } + + public function testDecodesGzippedResponses() + { + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['decode_content' => true], + ]); + $response->wait(); + $this->assertEquals('test', Core::body($response)); + $this->assertEquals('', $_SERVER['_curl'][CURLOPT_ENCODING]); + $sent = Server::received()[0]; + $this->assertNull(Core::header($sent, 'Accept-Encoding')); + } + + public function testDecodesGzippedResponsesWithHeader() + { + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => [ + 'host' => [Server::$host], + 'Accept-Encoding' => ['gzip'], + ], + 'client' => ['decode_content' => true], + ]); + $response->wait(); + $this->assertEquals('gzip', $_SERVER['_curl'][CURLOPT_ENCODING]); + $sent = Server::received()[0]; + $this->assertEquals('gzip', Core::header($sent, 'Accept-Encoding')); + $this->assertEquals('test', Core::body($response)); + } + + public function testDoesNotForceDecode() + { + $content = $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['decode_content' => false], + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertNull(Core::header($sent, 'Accept-Encoding')); + $this->assertEquals($content, Core::body($response)); + } + + public function testProtocolVersion() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'version' => 1.0, + ]); + $this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesSaveTo() + { + $handler = new CurlMultiHandler(); + $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['save_to' => true], + ]); + } + + public function testSavesToStream() + { + $stream = fopen('php://memory', 'r+'); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'decode_content' => true, + 'save_to' => $stream, + ], + ]); + $response->wait(); + rewind($stream); + $this->assertEquals('test', stream_get_contents($stream)); + } + + public function testSavesToGuzzleStream() + { + $stream = Stream::factory(); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'decode_content' => true, + 'save_to' => $stream, + ], + ]); + $response->wait(); + $this->assertEquals('test', (string) $stream); + } + + public function testSavesToFileOnDisk() + { + $tmpfile = tempnam(sys_get_temp_dir(), 'testfile'); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'decode_content' => true, + 'save_to' => $tmpfile, + ], + ]); + $response->wait(); + $this->assertEquals('test', file_get_contents($tmpfile)); + unlink($tmpfile); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesBody() + { + $handler = new CurlMultiHandler(); + $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => false, + ]); + } + + public function testAddsLargePayloadFromStreamWithNoSizeUsingChunked() + { + $stream = Stream::factory('foo'); + $stream = FnStream::decorate($stream, [ + 'getSize' => function () { + return null; + } + ]); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => $stream, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding')); + $this->assertNull(Core::header($sent, 'Content-Length')); + $this->assertEquals('foo', $sent['body']); + } + + public function testAddsPayloadFromIterator() + { + $iter = new \ArrayIterator(['f', 'o', 'o']); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => $iter, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding')); + $this->assertNull(Core::header($sent, 'Content-Length')); + $this->assertEquals('foo', $sent['body']); + } + + public function testAddsPayloadFromResource() + { + $res = fopen('php://memory', 'r+'); + $data = str_repeat('.', 1000000); + fwrite($res, $data); + rewind($res); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => [ + 'host' => [Server::$host], + 'content-length' => [1000000], + ], + 'body' => $res, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertNull(Core::header($sent, 'Transfer-Encoding')); + $this->assertEquals(1000000, Core::header($sent, 'Content-Length')); + $this->assertEquals($data, $sent['body']); + } + + public function testAddsContentLengthFromStream() + { + $stream = Stream::factory('foo'); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => $stream, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals(3, Core::header($sent, 'Content-Length')); + $this->assertNull(Core::header($sent, 'Transfer-Encoding')); + $this->assertEquals('foo', $sent['body']); + } + + public function testDoesNotAddMultipleContentLengthHeaders() + { + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => [ + 'host' => [Server::$host], + 'content-length' => [3], + ], + 'body' => 'foo', + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals(3, Core::header($sent, 'Content-Length')); + $this->assertNull(Core::header($sent, 'Transfer-Encoding')); + $this->assertEquals('foo', $sent['body']); + } + + public function testSendsPostWithNoBodyOrDefaultContentType() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'POST', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + ]); + $response->wait(); + $received = Server::received()[0]; + $this->assertEquals('POST', $received['http_method']); + $this->assertNull(Core::header($received, 'content-type')); + $this->assertSame('0', Core::firstHeader($received, 'content-length')); + } + + public function testParseProtocolVersion() + { + $res = CurlFactory::createResponse( + function () {}, + [], + ['curl' => ['errno' => null]], + ['HTTP/1.1 200 Ok'], + null + ); + + $this->assertSame('1.1', $res['version']); + } + + public function testFailsWhenNoResponseAndNoBody() + { + $res = CurlFactory::createResponse(function () {}, [], [], [], null); + $this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']); + $this->assertContains( + 'No response was received for a request with no body', + $res['error']->getMessage() + ); + } + + public function testFailsWhenCannotRewindRetry() + { + $res = CurlFactory::createResponse(function () {}, [ + 'body' => new NoSeekStream(Stream::factory('foo')) + ], [], [], null); + $this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']); + $this->assertContains( + 'rewind the request body failed', + $res['error']->getMessage() + ); + } + + public function testRetriesWhenBodyCanBeRewound() + { + $callHandler = $called = false; + $res = CurlFactory::createResponse(function () use (&$callHandler) { + $callHandler = true; + return ['status' => 200]; + }, [ + 'body' => FnStream::decorate(Stream::factory('test'), [ + 'seek' => function () use (&$called) { + $called = true; + return true; + } + ]) + ], [], [], null); + + $this->assertTrue($callHandler); + $this->assertTrue($called); + $this->assertEquals('200', $res['status']); + } + + public function testFailsWhenRetryMoreThanThreeTimes() + { + $call = 0; + $mock = new MockHandler(function (array $request) use (&$mock, &$call) { + $call++; + return CurlFactory::createResponse($mock, $request, [], [], null); + }); + $response = $mock([ + 'http_method' => 'GET', + 'body' => 'test', + ]); + $this->assertEquals(3, $call); + $this->assertArrayHasKey('error', $response); + $this->assertContains( + 'The cURL request was retried 3 times', + $response['error']->getMessage() + ); + } + + public function testHandles100Continue() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => '200', + 'reason' => 'OK', + 'headers' => [ + 'Test' => ['Hello'], + 'Content-Length' => ['4'], + ], + 'body' => 'test', + ], + ]); + + $request = [ + 'http_method' => 'PUT', + 'headers' => [ + 'Host' => [Server::$host], + 'Expect' => ['100-Continue'], + ], + 'body' => 'test', + ]; + + $handler = new CurlMultiHandler(); + $response = $handler($request)->wait(); + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals(['Hello'], $response['headers']['Test']); + $this->assertEquals(['4'], $response['headers']['Content-Length']); + $this->assertEquals('test', Core::body($response)); + } + + public function testCreatesConnectException() + { + $m = new \ReflectionMethod('GuzzleHttp\Ring\Client\CurlFactory', 'createErrorResponse'); + $m->setAccessible(true); + $response = $m->invoke( + null, + function () {}, + [], + [ + 'err_message' => 'foo', + 'curl' => [ + 'errno' => CURLE_COULDNT_CONNECT, + ] + ] + ); + $this->assertInstanceOf('GuzzleHttp\Ring\Exception\ConnectException', $response['error']); + } + + public function testParsesLastResponseOnly() + { + $response1 = [ + 'status' => 301, + 'headers' => [ + 'Content-Length' => ['0'], + 'Location' => ['/foo'] + ] + ]; + + $response2 = [ + 'status' => 200, + 'headers' => [ + 'Content-Length' => ['0'], + 'Foo' => ['bar'] + ] + ]; + + Server::flush(); + Server::enqueue([$response1, $response2]); + + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['Host' => [Server::$host]], + 'client' => [ + 'curl' => [ + CURLOPT_FOLLOWLOCATION => true + ] + ] + ])->wait(); + + $this->assertEquals(1, $response['transfer_stats']['redirect_count']); + $this->assertEquals('http://127.0.0.1:8125/foo', $response['effective_url']); + $this->assertEquals(['bar'], $response['headers']['Foo']); + $this->assertEquals(200, $response['status']); + $this->assertFalse(Core::hasHeader($response, 'Location')); + } + + public function testMaintainsMultiHeaderOrder() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => 200, + 'headers' => [ + 'Content-Length' => ['0'], + 'Foo' => ['a', 'b'], + 'foo' => ['c', 'd'], + ] + ] + ]); + + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['Host' => [Server::$host]] + ])->wait(); + + $this->assertEquals( + ['a', 'b', 'c', 'd'], + Core::headerLines($response, 'Foo') + ); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Directory /path/to/does/not does not exist for save_to value of /path/to/does/not/exist.txt + */ + public function testThrowsWhenDirNotFound() + { + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$url]], + 'client' => ['save_to' => '/path/to/does/not/exist.txt'], + ]; + + $f = new CurlFactory(); + $f($request); + } +} + +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php new file mode 100644 index 00000000..ba03b8cd --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php @@ -0,0 +1,96 @@ +markTestSkipped('curl_reset() is not available'); + } + } + + protected function getHandler($factory = null, $options = []) + { + return new CurlHandler($options); + } + + public function testCanSetMaxHandles() + { + $a = new CurlHandler(['max_handles' => 10]); + $this->assertEquals(10, $this->readAttribute($a, 'maxHandles')); + } + + public function testCreatesCurlErrors() + { + $handler = new CurlHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['localhost:123']], + 'client' => ['timeout' => 0.001, 'connect_timeout' => 0.001], + ]); + $this->assertNull($response['status']); + $this->assertNull($response['reason']); + $this->assertEquals([], $response['headers']); + $this->assertInstanceOf( + 'GuzzleHttp\Ring\Exception\RingException', + $response['error'] + ); + + $this->assertEquals( + 1, + preg_match('/^cURL error \d+: .*$/', $response['error']->getMessage()) + ); + } + + public function testReleasesAdditionalEasyHandles() + { + Server::flush(); + $response = [ + 'status' => 200, + 'headers' => ['Content-Length' => [4]], + 'body' => 'test', + ]; + + Server::enqueue([$response, $response, $response, $response]); + $a = new CurlHandler(['max_handles' => 2]); + + $fn = function () use (&$calls, $a, &$fn) { + if (++$calls < 4) { + $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['progress' => $fn], + ]); + } + }; + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'progress' => $fn, + ], + ]; + + $a($request); + $this->assertCount(2, $this->readAttribute($a, 'handles')); + } + + public function testReusesHandles() + { + Server::flush(); + $response = ['status' => 200]; + Server::enqueue([$response, $response]); + $a = new CurlHandler(); + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]; + $a($request); + $a($request); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php new file mode 100644 index 00000000..530b2394 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php @@ -0,0 +1,181 @@ + 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(200, $response['status']); + $this->assertArrayHasKey('transfer_stats', $response); + $realUrl = trim($response['transfer_stats']['url'], '/'); + $this->assertEquals(trim(Server::$url, '/'), $realUrl); + $this->assertArrayHasKey('effective_url', $response); + $this->assertEquals( + trim(Server::$url, '/'), + trim($response['effective_url'], '/') + ); + } + + public function testCreatesErrorResponses() + { + $url = 'http://localhost:123/'; + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => ['localhost:123']], + ]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertNull($response['status']); + $this->assertNull($response['reason']); + $this->assertEquals([], $response['headers']); + $this->assertArrayHasKey('error', $response); + $this->assertContains('cURL error ', $response['error']->getMessage()); + $this->assertArrayHasKey('transfer_stats', $response); + $this->assertEquals( + trim($url, '/'), + trim($response['transfer_stats']['url'], '/') + ); + $this->assertArrayHasKey('effective_url', $response); + $this->assertEquals( + trim($url, '/'), + trim($response['effective_url'], '/') + ); + } + + public function testSendsFuturesWhenDestructed() + { + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $a->__destruct(); + $this->assertEquals(200, $response['status']); + } + + public function testCanSetMaxHandles() + { + $a = new CurlMultiHandler(['max_handles' => 2]); + $this->assertEquals(2, $this->readAttribute($a, 'maxHandles')); + } + + public function testCanSetSelectTimeout() + { + $a = new CurlMultiHandler(['select_timeout' => 2]); + $this->assertEquals(2, $this->readAttribute($a, 'selectTimeout')); + } + + public function testSendsFuturesWhenMaxHandlesIsReached() + { + $request = [ + 'http_method' => 'PUT', + 'headers' => ['host' => [Server::$host]], + 'future' => 'lazy', // passing this to control the test + ]; + $response = ['status' => 200]; + Server::flush(); + Server::enqueue([$response, $response, $response]); + $a = new CurlMultiHandler(['max_handles' => 3]); + for ($i = 0; $i < 5; $i++) { + $responses[] = $a($request); + } + $this->assertCount(3, Server::received()); + $responses[3]->cancel(); + $responses[4]->cancel(); + } + + public function testCanCancel() + { + Server::flush(); + $response = ['status' => 200]; + Server::enqueue(array_fill_keys(range(0, 10), $response)); + $a = new CurlMultiHandler(); + $responses = []; + + for ($i = 0; $i < 10; $i++) { + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'future' => 'lazy', + ]); + $response->cancel(); + $responses[] = $response; + } + + $this->assertCount(0, Server::received()); + + foreach ($responses as $response) { + $this->assertTrue($this->readAttribute($response, 'isRealized')); + } + } + + public function testCannotCancelFinished() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]); + $response->wait(); + $response->cancel(); + } + + public function testDelaysInParallel() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $expected = microtime(true) + (100 / 1000); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['delay' => 100], + ]); + $response->wait(); + $this->assertGreaterThanOrEqual($expected, microtime(true)); + } + + public function testSendsNonLazyFutures() + { + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'future' => true, + ]; + Server::flush(); + Server::enqueue([['status' => 202]]); + $a = new CurlMultiHandler(); + $response = $a($request); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(202, $response['status']); + } + + public function testExtractsErrors() + { + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => ['127.0.0.1:123']], + 'future' => true, + ]; + Server::flush(); + Server::enqueue([['status' => 202]]); + $a = new CurlMultiHandler(); + $response = $a($request); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(CURLE_COULDNT_CONNECT, $response['curl']['errno']); + $this->assertNotEmpty($response['curl']['error']); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php new file mode 100644 index 00000000..a47bb30b --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php @@ -0,0 +1,65 @@ + 200]); + $calledA = false; + $a = function (array $req) use (&$calledA, $future) { + $calledA = true; + return $future; + }; + $calledB = false; + $b = function (array $req) use (&$calledB) { $calledB = true; }; + $s = Middleware::wrapFuture($a, $b); + $s([]); + $this->assertTrue($calledA); + $this->assertFalse($calledB); + } + + public function testFutureCallsStreamingHandler() + { + $future = new CompletedFutureArray(['status' => 200]); + $calledA = false; + $a = function (array $req) use (&$calledA) { $calledA = true; }; + $calledB = false; + $b = function (array $req) use (&$calledB, $future) { + $calledB = true; + return $future; + }; + $s = Middleware::wrapFuture($a, $b); + $result = $s(['client' => ['future' => true]]); + $this->assertFalse($calledA); + $this->assertTrue($calledB); + $this->assertSame($future, $result); + } + + public function testStreamingCallsDefaultHandler() + { + $calledA = false; + $a = function (array $req) use (&$calledA) { $calledA = true; }; + $calledB = false; + $b = function (array $req) use (&$calledB) { $calledB = true; }; + $s = Middleware::wrapStreaming($a, $b); + $s([]); + $this->assertTrue($calledA); + $this->assertFalse($calledB); + } + + public function testStreamingCallsStreamingHandler() + { + $calledA = false; + $a = function (array $req) use (&$calledA) { $calledA = true; }; + $calledB = false; + $b = function (array $req) use (&$calledB) { $calledB = true; }; + $s = Middleware::wrapStreaming($a, $b); + $s(['client' => ['stream' => true]]); + $this->assertFalse($calledA); + $this->assertTrue($calledB); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php new file mode 100644 index 00000000..26bcd6cd --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php @@ -0,0 +1,86 @@ + 200]); + $response = $mock([]); + $this->assertEquals(200, $response['status']); + $this->assertEquals([], $response['headers']); + $this->assertNull($response['body']); + $this->assertNull($response['reason']); + $this->assertNull($response['effective_url']); + } + + public function testReturnsFutures() + { + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred) { + $deferred->resolve(['status' => 200]); + } + ); + $mock = new MockHandler($future); + $response = $mock([]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(200, $response['status']); + } + + public function testReturnsFuturesWithThenCall() + { + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred) { + $deferred->resolve(['status' => 200]); + } + ); + $mock = new MockHandler($future); + $response = $mock([]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(200, $response['status']); + $req = null; + $promise = $response->then(function ($value) use (&$req) { + $req = $value; + $this->assertEquals(200, $req['status']); + }); + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + $this->assertEquals(200, $req['status']); + } + + public function testReturnsFuturesAndProxiesCancel() + { + $c = null; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () {}, + function () use (&$c) { + $c = true; + return true; + } + ); + $mock = new MockHandler($future); + $response = $mock([]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $response->cancel(); + $this->assertTrue($c); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Response must be an array or FutureArrayInterface. Found + */ + public function testEnsuresMockIsValid() + { + $mock = new MockHandler('foo'); + $mock([]); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/Server.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/Server.php new file mode 100644 index 00000000..14665a55 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/Server.php @@ -0,0 +1,183 @@ + [], 'reason' => '', 'body' => '']; + $data[] = $response; + } + + self::send('PUT', '/guzzle-server/responses', json_encode($data)); + } + + /** + * Get all of the received requests as a RingPHP request structure. + * + * @return array + * @throws \RuntimeException + */ + public static function received() + { + if (!self::$started) { + return []; + } + + $response = self::send('GET', '/guzzle-server/requests'); + $body = Core::body($response); + $result = json_decode($body, true); + if ($result === false) { + throw new \RuntimeException('Error decoding response: ' + . json_last_error()); + } + + foreach ($result as &$res) { + if (isset($res['uri'])) { + $res['resource'] = $res['uri']; + } + if (isset($res['query_string'])) { + $res['resource'] .= '?' . $res['query_string']; + } + if (!isset($res['resource'])) { + $res['resource'] = ''; + } + // Ensure that headers are all arrays + if (isset($res['headers'])) { + foreach ($res['headers'] as &$h) { + $h = (array) $h; + } + unset($h); + } + } + + unset($res); + return $result; + } + + /** + * Stop running the node.js server + */ + public static function stop() + { + if (self::$started) { + self::send('DELETE', '/guzzle-server'); + } + + self::$started = false; + } + + public static function wait($maxTries = 20) + { + $tries = 0; + while (!self::isListening() && ++$tries < $maxTries) { + usleep(100000); + } + + if (!self::isListening()) { + throw new \RuntimeException('Unable to contact node.js server'); + } + } + + public static function start() + { + if (self::$started) { + return; + } + + try { + self::wait(); + } catch (\Exception $e) { + exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' + . self::$port . ' >> /tmp/server.log 2>&1 &'); + self::wait(); + } + + self::$started = true; + } + + private static function isListening() + { + $response = self::send('GET', '/guzzle-server/perf', null, [ + 'connect_timeout' => 1, + 'timeout' => 1 + ]); + + return !isset($response['error']); + } + + private static function send( + $method, + $path, + $body = null, + array $client = [] + ) { + $handler = new StreamHandler(); + + $request = [ + 'http_method' => $method, + 'uri' => $path, + 'request_port' => 8125, + 'headers' => ['host' => ['127.0.0.1:8125']], + 'body' => $body, + 'client' => $client, + ]; + + if ($body) { + $request['headers']['content-length'] = [strlen($body)]; + } + + return $handler($request); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php new file mode 100644 index 00000000..3cb9a8e1 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php @@ -0,0 +1,480 @@ +queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => [ + 'host' => [Server::$host], + 'Foo' => ['Bar'], + ], + ]); + + $this->assertEquals('1.1', $response['version']); + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals(['Bar'], $response['headers']['Foo']); + $this->assertEquals(['8'], $response['headers']['Content-Length']); + $this->assertEquals('hi there', Core::body($response)); + + $sent = Server::received()[0]; + $this->assertEquals('GET', $sent['http_method']); + $this->assertEquals('/', $sent['resource']); + $this->assertEquals(['127.0.0.1:8125'], $sent['headers']['host']); + $this->assertEquals('Bar', Core::header($sent, 'foo')); + } + + public function testAddsErrorToResponse() + { + $handler = new StreamHandler(); + $result = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => ['localhost:123']], + 'client' => ['timeout' => 0.01], + ]); + $this->assertInstanceOf( + 'GuzzleHttp\Ring\Future\CompletedFutureArray', + $result + ); + $this->assertNull($result['status']); + $this->assertNull($result['body']); + $this->assertEquals([], $result['headers']); + $this->assertInstanceOf( + 'GuzzleHttp\Ring\Exception\RingException', + $result['error'] + ); + } + + public function testEnsuresTheHttpProtocol() + { + $handler = new StreamHandler(); + $result = $handler([ + 'http_method' => 'GET', + 'url' => 'ftp://localhost:123', + ]); + $this->assertArrayHasKey('error', $result); + $this->assertContains( + 'URL is invalid: ftp://localhost:123', + $result['error']->getMessage() + ); + } + + public function testStreamAttributeKeepsStreamOpen() + { + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'PUT', + 'uri' => '/foo', + 'query_string' => 'baz=bar', + 'headers' => [ + 'host' => [Server::$host], + 'Foo' => ['Bar'], + ], + 'body' => 'test', + 'client' => ['stream' => true], + ]); + + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals('8', Core::header($response, 'Content-Length')); + $body = $response['body']; + $this->assertTrue(is_resource($body)); + $this->assertEquals('http', stream_get_meta_data($body)['wrapper_type']); + $this->assertEquals('hi there', stream_get_contents($body)); + fclose($body); + $sent = Server::received()[0]; + $this->assertEquals('PUT', $sent['http_method']); + $this->assertEquals('/foo', $sent['uri']); + $this->assertEquals('baz=bar', $sent['query_string']); + $this->assertEquals('/foo?baz=bar', $sent['resource']); + $this->assertEquals('127.0.0.1:8125', Core::header($sent, 'host')); + $this->assertEquals('Bar', Core::header($sent, 'foo')); + } + + public function testDrainsResponseIntoTempStream() + { + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + ]); + $body = $response['body']; + $this->assertEquals('php://temp', stream_get_meta_data($body)['uri']); + $this->assertEquals('hi', fread($body, 2)); + fclose($body); + } + + public function testDrainsResponseIntoSaveToBody() + { + $r = fopen('php://temp', 'r+'); + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'client' => ['save_to' => $r], + ]); + $body = $response['body']; + $this->assertEquals('php://temp', stream_get_meta_data($body)['uri']); + $this->assertEquals('hi', fread($body, 2)); + $this->assertEquals(' there', stream_get_contents($r)); + fclose($r); + } + + public function testDrainsResponseIntoSaveToBodyAtPath() + { + $tmpfname = tempnam('/tmp', 'save_to_path'); + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'client' => ['save_to' => $tmpfname], + ]); + $body = $response['body']; + $this->assertInstanceOf('GuzzleHttp\Stream\StreamInterface', $body); + $this->assertEquals($tmpfname, $body->getMetadata('uri')); + $this->assertEquals('hi', $body->read(2)); + $body->close(); + unlink($tmpfname); + } + + public function testAutomaticallyDecompressGzip() + { + Server::flush(); + $content = gzencode('test'); + Server::enqueue([ + [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => [ + 'Content-Encoding' => ['gzip'], + 'Content-Length' => [strlen($content)], + ], + 'body' => $content, + ], + ]); + + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/', + 'client' => ['decode_content' => true], + ]); + $this->assertEquals('test', Core::body($response)); + } + + public function testDoesNotForceGzipDecode() + { + Server::flush(); + $content = gzencode('test'); + Server::enqueue([ + [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => [ + 'Content-Encoding' => ['gzip'], + 'Content-Length' => [strlen($content)], + ], + 'body' => $content, + ], + ]); + + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/', + 'client' => ['stream' => true, 'decode_content' => false], + ]); + $this->assertSame($content, Core::body($response)); + } + + public function testProtocolVersion() + { + $this->queueRes(); + $handler = new StreamHandler(); + $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'version' => 1.0, + ]); + + $this->assertEquals(1.0, Server::received()[0]['version']); + } + + protected function getSendResult(array $opts) + { + $this->queueRes(); + $handler = new StreamHandler(); + $opts['stream'] = true; + return $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'client' => $opts, + ]); + } + + public function testAddsProxy() + { + $res = $this->getSendResult(['stream' => true, 'proxy' => '127.0.0.1:8125']); + $opts = stream_context_get_options($res['body']); + $this->assertEquals('127.0.0.1:8125', $opts['http']['proxy']); + } + + public function testAddsTimeout() + { + $res = $this->getSendResult(['stream' => true, 'timeout' => 200]); + $opts = stream_context_get_options($res['body']); + $this->assertEquals(200, $opts['http']['timeout']); + } + + public function testVerifiesVerifyIsValidIfPath() + { + $res = $this->getSendResult(['verify' => '/does/not/exist']); + $this->assertContains( + 'SSL CA bundle not found: /does/not/exist', + (string) $res['error'] + ); + } + + public function testVerifyCanBeDisabled() + { + $res = $this->getSendResult(['verify' => false]); + $this->assertArrayNotHasKey('error', $res); + } + + public function testVerifiesCertIfValidPath() + { + $res = $this->getSendResult(['cert' => '/does/not/exist']); + $this->assertContains( + 'SSL certificate not found: /does/not/exist', + (string) $res['error'] + ); + } + + public function testVerifyCanBeSetToPath() + { + $path = $path = ClientUtils::getDefaultCaBundle(); + $res = $this->getSendResult(['verify' => $path]); + $this->assertArrayNotHasKey('error', $res); + $opts = stream_context_get_options($res['body']); + $this->assertEquals(true, $opts['ssl']['verify_peer']); + $this->assertEquals($path, $opts['ssl']['cafile']); + $this->assertTrue(file_exists($opts['ssl']['cafile'])); + } + + public function testUsesSystemDefaultBundle() + { + $path = $path = ClientUtils::getDefaultCaBundle(); + $res = $this->getSendResult(['verify' => true]); + $this->assertArrayNotHasKey('error', $res); + $opts = stream_context_get_options($res['body']); + if (PHP_VERSION_ID < 50600) { + $this->assertEquals($path, $opts['ssl']['cafile']); + } + } + + public function testEnsuresVerifyOptionIsValid() + { + $res = $this->getSendResult(['verify' => 10]); + $this->assertContains( + 'Invalid verify request option', + (string) $res['error'] + ); + } + + public function testCanSetPasswordWhenSettingCert() + { + $path = __FILE__; + $res = $this->getSendResult(['cert' => [$path, 'foo']]); + $opts = stream_context_get_options($res['body']); + $this->assertEquals($path, $opts['ssl']['local_cert']); + $this->assertEquals('foo', $opts['ssl']['passphrase']); + } + + public function testDebugAttributeWritesToStream() + { + $this->queueRes(); + $f = fopen('php://temp', 'w+'); + $this->getSendResult(['debug' => $f]); + fseek($f, 0); + $contents = stream_get_contents($f); + $this->assertContains(' [CONNECT]', $contents); + $this->assertContains(' [FILE_SIZE_IS]', $contents); + $this->assertContains(' [PROGRESS]', $contents); + } + + public function testDebugAttributeWritesStreamInfoToBuffer() + { + $called = false; + $this->queueRes(); + $buffer = fopen('php://temp', 'r+'); + $this->getSendResult([ + 'progress' => function () use (&$called) { $called = true; }, + 'debug' => $buffer, + ]); + fseek($buffer, 0); + $contents = stream_get_contents($buffer); + $this->assertContains(' [CONNECT]', $contents); + $this->assertContains(' [FILE_SIZE_IS] message: "Content-Length: 8"', $contents); + $this->assertContains(' [PROGRESS] bytes_max: "8"', $contents); + $this->assertTrue($called); + } + + public function testEmitsProgressInformation() + { + $called = []; + $this->queueRes(); + $this->getSendResult([ + 'progress' => function () use (&$called) { + $called[] = func_get_args(); + }, + ]); + $this->assertNotEmpty($called); + $this->assertEquals(8, $called[0][0]); + $this->assertEquals(0, $called[0][1]); + } + + public function testEmitsProgressInformationAndDebugInformation() + { + $called = []; + $this->queueRes(); + $buffer = fopen('php://memory', 'w+'); + $this->getSendResult([ + 'debug' => $buffer, + 'progress' => function () use (&$called) { + $called[] = func_get_args(); + }, + ]); + $this->assertNotEmpty($called); + $this->assertEquals(8, $called[0][0]); + $this->assertEquals(0, $called[0][1]); + rewind($buffer); + $this->assertNotEmpty(stream_get_contents($buffer)); + fclose($buffer); + } + + public function testAddsProxyByProtocol() + { + $url = str_replace('http', 'tcp', Server::$url); + $res = $this->getSendResult(['proxy' => ['http' => $url]]); + $opts = stream_context_get_options($res['body']); + $this->assertEquals($url, $opts['http']['proxy']); + } + + public function testPerformsShallowMergeOfCustomContextOptions() + { + $res = $this->getSendResult([ + 'stream_context' => [ + 'http' => [ + 'request_fulluri' => true, + 'method' => 'HEAD', + ], + 'socket' => [ + 'bindto' => '127.0.0.1:0', + ], + 'ssl' => [ + 'verify_peer' => false, + ], + ], + ]); + + $opts = stream_context_get_options($res['body']); + $this->assertEquals('HEAD', $opts['http']['method']); + $this->assertTrue($opts['http']['request_fulluri']); + $this->assertFalse($opts['ssl']['verify_peer']); + $this->assertEquals('127.0.0.1:0', $opts['socket']['bindto']); + } + + public function testEnsuresThatStreamContextIsAnArray() + { + $res = $this->getSendResult(['stream_context' => 'foo']); + $this->assertContains( + 'stream_context must be an array', + (string) $res['error'] + ); + } + + public function testDoesNotAddContentTypeByDefault() + { + $this->queueRes(); + $handler = new StreamHandler(); + $handler([ + 'http_method' => 'PUT', + 'uri' => '/', + 'headers' => ['host' => [Server::$host], 'content-length' => [3]], + 'body' => 'foo', + ]); + $req = Server::received()[0]; + $this->assertEquals('', Core::header($req, 'Content-Type')); + $this->assertEquals(3, Core::header($req, 'Content-Length')); + } + + private function queueRes() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => [ + 'Foo' => ['Bar'], + 'Content-Length' => [8], + ], + 'body' => 'hi there', + ], + ]); + } + + public function testSupports100Continue() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => '200', + 'reason' => 'OK', + 'headers' => [ + 'Test' => ['Hello'], + 'Content-Length' => ['4'], + ], + 'body' => 'test', + ], + ]); + + $request = [ + 'http_method' => 'PUT', + 'headers' => [ + 'Host' => [Server::$host], + 'Expect' => ['100-Continue'], + ], + 'body' => 'test', + ]; + + $handler = new StreamHandler(); + $response = $handler($request); + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals(['Hello'], $response['headers']['Test']); + $this->assertEquals(['4'], $response['headers']['Content-Length']); + $this->assertEquals('test', Core::body($response)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/server.js b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/server.js new file mode 100644 index 00000000..6a03e33a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Client/server.js @@ -0,0 +1,241 @@ +/** + * Guzzle node.js test server to return queued responses to HTTP requests and + * expose a RESTful API for enqueueing responses and retrieving the requests + * that have been received. + * + * - Delete all requests that have been received: + * > DELETE /guzzle-server/requests + * > Host: 127.0.0.1:8125 + * + * - Enqueue responses + * > PUT /guzzle-server/responses + * > Host: 127.0.0.1:8125 + * > + * > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }] + * + * - Get the received requests + * > GET /guzzle-server/requests + * > Host: 127.0.0.1:8125 + * + * < HTTP/1.1 200 OK + * < + * < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}] + * + * - Attempt access to the secure area + * > GET /secure/by-digest/qop-auth/guzzle-server/requests + * > Host: 127.0.0.1:8125 + * + * < HTTP/1.1 401 Unauthorized + * < WWW-Authenticate: Digest realm="Digest Test", qop="auth", nonce="0796e98e1aeef43141fab2a66bf4521a", algorithm="MD5", stale="false" + * < + * < 401 Unauthorized + * + * - Shutdown the server + * > DELETE /guzzle-server + * > Host: 127.0.0.1:8125 + * + * @package Guzzle PHP + * @license See the LICENSE file that was distributed with this source code. + */ + +var http = require('http'); +var url = require('url'); + +/** + * Guzzle node.js server + * @class + */ +var GuzzleServer = function(port, log) { + + this.port = port; + this.log = log; + this.responses = []; + this.requests = []; + var that = this; + + var md5 = function(input) { + var crypto = require('crypto'); + var hasher = crypto.createHash('md5'); + hasher.update(input); + return hasher.digest('hex'); + } + + /** + * Node.js HTTP server authentication module. + * + * It is only initialized on demand (by loadAuthentifier). This avoids + * requiring the dependency to http-auth on standard operations, and the + * performance hit at startup. + */ + var auth; + + /** + * Provides authentication handlers (Basic, Digest). + */ + var loadAuthentifier = function(type, options) { + var typeId = type; + if (type == 'digest') { + typeId += '.'+(options && options.qop ? options.qop : 'none'); + } + if (!loadAuthentifier[typeId]) { + if (!auth) { + try { + auth = require('http-auth'); + } catch (e) { + if (e.code == 'MODULE_NOT_FOUND') { + return; + } + } + } + switch (type) { + case 'digest': + var digestParams = { + realm: 'Digest Test', + login: 'me', + password: 'test' + }; + if (options && options.qop) { + digestParams.qop = options.qop; + } + loadAuthentifier[typeId] = auth.digest(digestParams, function(username, callback) { + callback(md5(digestParams.login + ':' + digestParams.realm + ':' + digestParams.password)); + }); + break + } + } + return loadAuthentifier[typeId]; + }; + + var firewallRequest = function(request, req, res, requestHandlerCallback) { + var securedAreaUriParts = request.uri.match(/^\/secure\/by-(digest)(\/qop-([^\/]*))?(\/.*)$/); + if (securedAreaUriParts) { + var authentifier = loadAuthentifier(securedAreaUriParts[1], { qop: securedAreaUriParts[2] }); + if (!authentifier) { + res.writeHead(501, 'HTTP authentication not implemented', { 'Content-Length': 0 }); + res.end(); + return; + } + authentifier.check(req, res, function(req, res) { + req.url = securedAreaUriParts[4]; + requestHandlerCallback(request, req, res); + }); + } else { + requestHandlerCallback(request, req, res); + } + }; + + var controlRequest = function(request, req, res) { + if (req.url == '/guzzle-server/perf') { + res.writeHead(200, 'OK', {'Content-Length': 16}); + res.end('Body of response'); + } else if (req.method == 'DELETE') { + if (req.url == '/guzzle-server/requests') { + // Clear the received requests + that.requests = []; + res.writeHead(200, 'OK', { 'Content-Length': 0 }); + res.end(); + if (that.log) { + console.log('Flushing requests'); + } + } else if (req.url == '/guzzle-server') { + // Shutdown the server + res.writeHead(200, 'OK', { 'Content-Length': 0, 'Connection': 'close' }); + res.end(); + if (that.log) { + console.log('Shutting down'); + } + that.server.close(); + } + } else if (req.method == 'GET') { + if (req.url === '/guzzle-server/requests') { + if (that.log) { + console.log('Sending received requests'); + } + // Get received requests + var body = JSON.stringify(that.requests); + res.writeHead(200, 'OK', { 'Content-Length': body.length }); + res.end(body); + } + } else if (req.method == 'PUT' && req.url == '/guzzle-server/responses') { + if (that.log) { + console.log('Adding responses...'); + } + if (!request.body) { + if (that.log) { + console.log('No response data was provided'); + } + res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 }); + } else { + that.responses = eval('(' + request.body + ')'); + for (var i = 0; i < that.responses.length; i++) { + if (that.responses[i].body) { + that.responses[i].body = new Buffer(that.responses[i].body, 'base64'); + } + } + if (that.log) { + console.log(that.responses); + } + res.writeHead(200, 'OK', { 'Content-Length': 0 }); + } + res.end(); + } + }; + + var receivedRequest = function(request, req, res) { + if (req.url.indexOf('/guzzle-server') === 0) { + controlRequest(request, req, res); + } else if (req.url.indexOf('/guzzle-server') == -1 && !that.responses.length) { + res.writeHead(500); + res.end('No responses in queue'); + } else { + if (that.log) { + console.log('Returning response from queue and adding request'); + } + that.requests.push(request); + var response = that.responses.shift(); + res.writeHead(response.status, response.reason, response.headers); + res.end(response.body); + } + }; + + this.start = function() { + + that.server = http.createServer(function(req, res) { + + var parts = url.parse(req.url, false); + var request = { + http_method: req.method, + scheme: parts.scheme, + uri: parts.pathname, + query_string: parts.query, + headers: req.headers, + version: req.httpVersion, + body: '' + }; + + // Receive each chunk of the request body + req.addListener('data', function(chunk) { + request.body += chunk; + }); + + // Called when the request completes + req.addListener('end', function() { + firewallRequest(request, req, res, receivedRequest); + }); + }); + + that.server.listen(this.port, '127.0.0.1'); + + if (this.log) { + console.log('Server running at http://127.0.0.1:8125/'); + } + }; +}; + +// Get the port from the arguments +port = process.argv.length >= 3 ? process.argv[2] : 8125; +log = process.argv.length >= 4 ? process.argv[3] : false; + +// Start the server +server = new GuzzleServer(port, log); +server.start(); diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/CoreTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/CoreTest.php new file mode 100644 index 00000000..49522f26 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/CoreTest.php @@ -0,0 +1,336 @@ +assertNull(Core::header([], 'Foo')); + $this->assertNull(Core::firstHeader([], 'Foo')); + } + + public function testChecksIfHasHeader() + { + $message = [ + 'headers' => [ + 'Foo' => ['Bar', 'Baz'], + 'foo' => ['hello'], + 'bar' => ['1'] + ] + ]; + $this->assertTrue(Core::hasHeader($message, 'Foo')); + $this->assertTrue(Core::hasHeader($message, 'foo')); + $this->assertTrue(Core::hasHeader($message, 'FoO')); + $this->assertTrue(Core::hasHeader($message, 'bar')); + $this->assertFalse(Core::hasHeader($message, 'barr')); + } + + public function testReturnsFirstHeaderWhenSimple() + { + $this->assertEquals('Bar', Core::firstHeader([ + 'headers' => ['Foo' => ['Bar', 'Baz']], + ], 'Foo')); + } + + public function testReturnsFirstHeaderWhenMultiplePerLine() + { + $this->assertEquals('Bar', Core::firstHeader([ + 'headers' => ['Foo' => ['Bar, Baz']], + ], 'Foo')); + } + + public function testExtractsCaseInsensitiveHeader() + { + $this->assertEquals( + 'hello', + Core::header(['headers' => ['foo' => ['hello']]], 'FoO') + ); + } + + public function testExtractsCaseInsensitiveHeaderLines() + { + $this->assertEquals( + ['a', 'b', 'c', 'd'], + Core::headerLines([ + 'headers' => [ + 'foo' => ['a', 'b'], + 'Foo' => ['c', 'd'] + ] + ], 'foo') + ); + } + + public function testExtractsHeaderLines() + { + $this->assertEquals( + ['bar', 'baz'], + Core::headerLines([ + 'headers' => [ + 'Foo' => ['bar', 'baz'], + ], + ], 'Foo') + ); + } + + public function testExtractsHeaderAsString() + { + $this->assertEquals( + 'bar, baz', + Core::header([ + 'headers' => [ + 'Foo' => ['bar', 'baz'], + ], + ], 'Foo', true) + ); + } + + public function testReturnsNullWhenHeaderNotFound() + { + $this->assertNull(Core::header(['headers' => []], 'Foo')); + } + + public function testRemovesHeaders() + { + $message = [ + 'headers' => [ + 'foo' => ['bar'], + 'Foo' => ['bam'], + 'baz' => ['123'], + ], + ]; + + $this->assertSame($message, Core::removeHeader($message, 'bam')); + $this->assertEquals([ + 'headers' => ['baz' => ['123']], + ], Core::removeHeader($message, 'foo')); + } + + public function testCreatesUrl() + { + $req = [ + 'scheme' => 'http', + 'headers' => ['host' => ['foo.com']], + 'uri' => '/', + ]; + + $this->assertEquals('http://foo.com/', Core::url($req)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage No Host header was provided + */ + public function testEnsuresHostIsAvailableWhenCreatingUrls() + { + Core::url([]); + } + + public function testCreatesUrlWithQueryString() + { + $req = [ + 'scheme' => 'http', + 'headers' => ['host' => ['foo.com']], + 'uri' => '/', + 'query_string' => 'foo=baz', + ]; + + $this->assertEquals('http://foo.com/?foo=baz', Core::url($req)); + } + + public function testUsesUrlIfSet() + { + $req = ['url' => 'http://foo.com']; + $this->assertEquals('http://foo.com', Core::url($req)); + } + + public function testReturnsNullWhenNoBody() + { + $this->assertNull(Core::body([])); + } + + public function testReturnsStreamAsString() + { + $this->assertEquals( + 'foo', + Core::body(['body' => Stream::factory('foo')]) + ); + } + + public function testReturnsString() + { + $this->assertEquals('foo', Core::body(['body' => 'foo'])); + } + + public function testReturnsResourceContent() + { + $r = fopen('php://memory', 'w+'); + fwrite($r, 'foo'); + rewind($r); + $this->assertEquals('foo', Core::body(['body' => $r])); + fclose($r); + } + + public function testReturnsIteratorContent() + { + $a = new \ArrayIterator(['a', 'b', 'cd', '']); + $this->assertEquals('abcd', Core::body(['body' => $a])); + } + + public function testReturnsObjectToString() + { + $this->assertEquals('foo', Core::body(['body' => new StrClass])); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresBodyIsValid() + { + Core::body(['body' => false]); + } + + public function testParsesHeadersFromLines() + { + $lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b']; + $this->assertEquals([ + 'Foo' => ['bar', 'baz'], + 'Abc' => ['123'], + 'Def' => ['a, b'], + ], Core::headersFromLines($lines)); + } + + public function testParsesHeadersFromLinesWithMultipleLines() + { + $lines = ['Foo: bar', 'Foo: baz', 'Foo: 123']; + $this->assertEquals([ + 'Foo' => ['bar', 'baz', '123'], + ], Core::headersFromLines($lines)); + } + + public function testCreatesArrayCallFunctions() + { + $called = []; + $a = function ($a, $b) use (&$called) { + $called['a'] = func_get_args(); + }; + $b = function ($a, $b) use (&$called) { + $called['b'] = func_get_args(); + }; + $c = Core::callArray([$a, $b]); + $c(1, 2); + $this->assertEquals([1, 2], $called['a']); + $this->assertEquals([1, 2], $called['b']); + } + + public function testRewindsGuzzleStreams() + { + $str = Stream::factory('foo'); + $this->assertTrue(Core::rewindBody(['body' => $str])); + } + + public function testRewindsStreams() + { + $str = Stream::factory('foo')->detach(); + $this->assertTrue(Core::rewindBody(['body' => $str])); + } + + public function testRewindsIterators() + { + $iter = new \ArrayIterator(['foo']); + $this->assertTrue(Core::rewindBody(['body' => $iter])); + } + + public function testRewindsStrings() + { + $this->assertTrue(Core::rewindBody(['body' => 'hi'])); + } + + public function testRewindsToStrings() + { + $this->assertTrue(Core::rewindBody(['body' => new StrClass()])); + } + + public function typeProvider() + { + return [ + ['foo', 'string(3) "foo"'], + [true, 'bool(true)'], + [false, 'bool(false)'], + [10, 'int(10)'], + [1.0, 'float(1)'], + [new StrClass(), 'object(GuzzleHttp\Tests\Ring\StrClass)'], + [['foo'], 'array(1)'] + ]; + } + + /** + * @dataProvider typeProvider + */ + public function testDescribesType($input, $output) + { + $this->assertEquals($output, Core::describeType($input)); + } + + public function testDoesSleep() + { + $t = microtime(true); + $expected = $t + (100 / 1000); + Core::doSleep(['client' => ['delay' => 100]]); + $this->assertGreaterThanOrEqual($expected, microtime(true)); + } + + public function testProxiesFuture() + { + $f = new CompletedFutureArray(['status' => 200]); + $res = null; + $proxied = Core::proxy($f, function ($value) use (&$res) { + $value['foo'] = 'bar'; + $res = $value; + return $value; + }); + $this->assertNotSame($f, $proxied); + $this->assertEquals(200, $f->wait()['status']); + $this->assertArrayNotHasKey('foo', $f->wait()); + $this->assertEquals('bar', $proxied->wait()['foo']); + $this->assertEquals(200, $proxied->wait()['status']); + } + + public function testProxiesDeferredFuture() + { + $d = new Deferred(); + $f = new FutureArray($d->promise()); + $f2 = Core::proxy($f); + $d->resolve(['foo' => 'bar']); + $this->assertEquals('bar', $f['foo']); + $this->assertEquals('bar', $f2['foo']); + } + + public function testProxiesDeferredFutureFailure() + { + $d = new Deferred(); + $f = new FutureArray($d->promise()); + $f2 = Core::proxy($f); + $d->reject(new \Exception('foo')); + try { + $f2['hello?']; + $this->fail('did not throw'); + } catch (\Exception $e) { + $this->assertEquals('foo', $e->getMessage()); + } + + } +} + +final class StrClass +{ + public function __toString() + { + return 'foo'; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php new file mode 100644 index 00000000..82d7efbf --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php @@ -0,0 +1,21 @@ + 'bar']); + $this->assertEquals('bar', $f['foo']); + $this->assertFalse(isset($f['baz'])); + $f['abc'] = '123'; + $this->assertTrue(isset($f['abc'])); + $this->assertEquals(['foo' => 'bar', 'abc' => '123'], iterator_to_array($f)); + $this->assertEquals(2, count($f)); + unset($f['abc']); + $this->assertEquals(1, count($f)); + $this->assertEquals(['foo' => 'bar'], iterator_to_array($f)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php new file mode 100644 index 00000000..6ded40df --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php @@ -0,0 +1,46 @@ +assertEquals('hi', $f->wait()); + $f->cancel(); + + $a = null; + $f->then(function ($v) use (&$a) { + $a = $v; + }); + $this->assertSame('hi', $a); + } + + public function testThrows() + { + $ex = new \Exception('foo'); + $f = new CompletedFutureValue(null, $ex); + $f->cancel(); + try { + $f->wait(); + $this->fail('did not throw'); + } catch (\Exception $e) { + $this->assertSame($e, $ex); + } + } + + public function testMarksAsCancelled() + { + $ex = new CancelledFutureAccessException(); + $f = new CompletedFutureValue(null, $ex); + try { + $f->wait(); + $this->fail('did not throw'); + } catch (\Exception $e) { + $this->assertSame($e, $ex); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php new file mode 100644 index 00000000..0e09f5af --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php @@ -0,0 +1,56 @@ +promise(), + function () use (&$c, $deferred) { + $c = true; + $deferred->resolve(['status' => 200]); + } + ); + $this->assertFalse($c); + $this->assertFalse($this->readAttribute($f, 'isRealized')); + $this->assertEquals(200, $f['status']); + $this->assertTrue($c); + } + + public function testActsLikeArray() + { + $deferred = new Deferred(); + $f = new FutureArray( + $deferred->promise(), + function () use (&$c, $deferred) { + $deferred->resolve(['status' => 200]); + } + ); + + $this->assertTrue(isset($f['status'])); + $this->assertEquals(200, $f['status']); + $this->assertEquals(['status' => 200], $f->wait()); + $this->assertEquals(1, count($f)); + $f['baz'] = 10; + $this->assertEquals(10, $f['baz']); + unset($f['baz']); + $this->assertFalse(isset($f['baz'])); + $this->assertEquals(['status' => 200], iterator_to_array($f)); + } + + /** + * @expectedException \RuntimeException + */ + public function testThrowsWhenAccessingInvalidProperty() + { + $deferred = new Deferred(); + $f = new FutureArray($deferred->promise(), function () {}); + $f->foo; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php new file mode 100644 index 00000000..d59c543d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php @@ -0,0 +1,109 @@ +promise(), + function () use ($deferred, &$called) { + $called++; + $deferred->resolve('foo'); + } + ); + + $this->assertEquals('foo', $f->wait()); + $this->assertEquals(1, $called); + $this->assertEquals('foo', $f->wait()); + $this->assertEquals(1, $called); + $f->cancel(); + $this->assertTrue($this->readAttribute($f, 'isRealized')); + } + + /** + * @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException + */ + public function testThrowsWhenAccessingCancelled() + { + $f = new FutureValue( + (new Deferred())->promise(), + function () {}, + function () { return true; } + ); + $f->cancel(); + $f->wait(); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testThrowsWhenDerefFailure() + { + $called = false; + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () use(&$called) { + $called = true; + } + ); + $deferred->reject(new \OutOfBoundsException()); + $f->wait(); + $this->assertFalse($called); + } + + /** + * @expectedException \GuzzleHttp\Ring\Exception\RingException + * @expectedExceptionMessage Waiting did not resolve future + */ + public function testThrowsWhenDerefDoesNotResolve() + { + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () use(&$called) { + $called = true; + } + ); + $f->wait(); + } + + public function testThrowingCancelledFutureAccessExceptionCancels() + { + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () use ($deferred) { + throw new CancelledFutureAccessException(); + } + ); + try { + $f->wait(); + $this->fail('did not throw'); + } catch (CancelledFutureAccessException $e) {} + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage foo + */ + public function testThrowingExceptionInDerefMarksAsFailed() + { + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () { + throw new \Exception('foo'); + } + ); + $f->wait(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/bootstrap.php b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/bootstrap.php new file mode 100644 index 00000000..017610fe --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/ringphp/tests/bootstrap.php @@ -0,0 +1,11 @@ + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/Makefile b/lib/Ebanx/vendor/guzzlehttp/streams/Makefile new file mode 100644 index 00000000..f4d42849 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/Makefile @@ -0,0 +1,19 @@ +all: clean coverage + +release: tag + git push origin --tags + +tag: + chag tag --sign --debug CHANGELOG.rst + +test: + vendor/bin/phpunit + +coverage: + vendor/bin/phpunit --coverage-html=artifacts/coverage + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/README.rst b/lib/Ebanx/vendor/guzzlehttp/streams/README.rst new file mode 100644 index 00000000..baff63b3 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/README.rst @@ -0,0 +1,36 @@ +============== +Guzzle Streams +============== + +Provides a simple abstraction over streams of data. + +This library is used in `Guzzle 5 `_, and is +(currently) compatible with the WIP PSR-7. + +Installation +============ + +This package can be installed easily using `Composer `_. +Simply add the following to the composer.json file at the root of your project: + +.. code-block:: javascript + + { + "require": { + "guzzlehttp/streams": "~3.0" + } + } + +Then install your dependencies using ``composer.phar install``. + +Documentation +============= + +The documentation for this package can be found on the main Guzzle website at +http://docs.guzzlephp.org/en/guzzle4/streams.html. + +Testing +======= + +This library is tested using PHPUnit. You'll need to install the dependencies +using `Composer `_ then run ``make test``. diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/composer.json b/lib/Ebanx/vendor/guzzlehttp/streams/composer.json new file mode 100644 index 00000000..6d703437 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzlehttp/streams", + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": ["stream", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "autoload": { + "psr-4": { "GuzzleHttp\\Stream\\": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/phpunit.xml.dist b/lib/Ebanx/vendor/guzzlehttp/streams/phpunit.xml.dist new file mode 100644 index 00000000..6e758c19 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + + tests + + + + + src + + src/functions.php + + + + diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/AppendStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/AppendStream.php new file mode 100644 index 00000000..94bda717 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/AppendStream.php @@ -0,0 +1,220 @@ +addStream($stream); + } + } + + public function __toString() + { + try { + $this->seek(0); + return $this->getContents(); + } catch (\Exception $e) { + return ''; + } + } + + /** + * Add a stream to the AppendStream + * + * @param StreamInterface $stream Stream to append. Must be readable. + * + * @throws \InvalidArgumentException if the stream is not readable + */ + public function addStream(StreamInterface $stream) + { + if (!$stream->isReadable()) { + throw new \InvalidArgumentException('Each stream must be readable'); + } + + // The stream is only seekable if all streams are seekable + if (!$stream->isSeekable()) { + $this->seekable = false; + } + + $this->streams[] = $stream; + } + + public function getContents() + { + return Utils::copyToString($this); + } + + /** + * Closes each attached stream. + * + * {@inheritdoc} + */ + public function close() + { + $this->pos = $this->current = 0; + + foreach ($this->streams as $stream) { + $stream->close(); + } + + $this->streams = []; + } + + /** + * Detaches each attached stream + * + * {@inheritdoc} + */ + public function detach() + { + $this->close(); + $this->detached = true; + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function tell() + { + return $this->pos; + } + + /** + * Tries to calculate the size by adding the size of each stream. + * + * If any of the streams do not return a valid number, then the size of the + * append stream cannot be determined and null is returned. + * + * {@inheritdoc} + */ + public function getSize() + { + $size = 0; + + foreach ($this->streams as $stream) { + $s = $stream->getSize(); + if ($s === null) { + return null; + } + $size += $s; + } + + return $size; + } + + public function eof() + { + return !$this->streams || + ($this->current >= count($this->streams) - 1 && + $this->streams[$this->current]->eof()); + } + + /** + * Attempts to seek to the given position. Only supports SEEK_SET. + * + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if (!$this->seekable || $whence !== SEEK_SET) { + return false; + } + + $success = true; + $this->pos = $this->current = 0; + + // Rewind each stream + foreach ($this->streams as $stream) { + if (!$stream->seek(0)) { + $success = false; + } + } + + if (!$success) { + return false; + } + + // Seek to the actual position by reading from each stream + while ($this->pos < $offset && !$this->eof()) { + $this->read(min(8096, $offset - $this->pos)); + } + + return $this->pos == $offset; + } + + /** + * Reads from all of the appended streams until the length is met or EOF. + * + * {@inheritdoc} + */ + public function read($length) + { + $buffer = ''; + $total = count($this->streams) - 1; + $remaining = $length; + + while ($remaining > 0) { + // Progress to the next stream if needed. + if ($this->streams[$this->current]->eof()) { + if ($this->current == $total) { + break; + } + $this->current++; + } + $buffer .= $this->streams[$this->current]->read($remaining); + $remaining = $length - strlen($buffer); + } + + $this->pos += strlen($buffer); + + return $buffer; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function write($string) + { + return false; + } + + public function getMetadata($key = null) + { + return $key ? null : []; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/AsyncReadStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/AsyncReadStream.php new file mode 100644 index 00000000..25ad9602 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/AsyncReadStream.php @@ -0,0 +1,207 @@ +isReadable() || !$buffer->isWritable()) { + throw new \InvalidArgumentException( + 'Buffer must be readable and writable' + ); + } + + if (isset($config['size'])) { + $this->size = $config['size']; + } + + static $callables = ['pump', 'drain']; + foreach ($callables as $check) { + if (isset($config[$check])) { + if (!is_callable($config[$check])) { + throw new \InvalidArgumentException( + $check . ' must be callable' + ); + } + $this->{$check} = $config[$check]; + } + } + + $this->hwm = $buffer->getMetadata('hwm'); + + // Cannot drain when there's no high water mark. + if ($this->hwm === null) { + $this->drain = null; + } + + $this->stream = $buffer; + } + + /** + * Factory method used to create new async stream and an underlying buffer + * if no buffer is provided. + * + * This function accepts the same options as AsyncReadStream::__construct, + * but added the following key value pairs: + * + * - buffer: (StreamInterface) Buffer used to buffer data. If none is + * provided, a default buffer is created. + * - hwm: (int) High water mark to use if a buffer is created on your + * behalf. + * - max_buffer: (int) If provided, wraps the utilized buffer in a + * DroppingStream decorator to ensure that buffer does not exceed a given + * length. When exceeded, the stream will begin dropping data. Set the + * max_buffer to 0, to use a NullStream which does not store data. + * - write: (callable) A function that is invoked when data is written + * to the underlying buffer. The function accepts the buffer as the first + * argument, and the data being written as the second. The function MUST + * return the number of bytes that were written or false to let writers + * know to slow down. + * - drain: (callable) See constructor documentation. + * - pump: (callable) See constructor documentation. + * + * @param array $options Associative array of options. + * + * @return array Returns an array containing the buffer used to buffer + * data, followed by the ready to use AsyncReadStream object. + */ + public static function create(array $options = []) + { + $maxBuffer = isset($options['max_buffer']) + ? $options['max_buffer'] + : null; + + if ($maxBuffer === 0) { + $buffer = new NullStream(); + } elseif (isset($options['buffer'])) { + $buffer = $options['buffer']; + } else { + $hwm = isset($options['hwm']) ? $options['hwm'] : 16384; + $buffer = new BufferStream($hwm); + } + + if ($maxBuffer > 0) { + $buffer = new DroppingStream($buffer, $options['max_buffer']); + } + + // Call the on_write callback if an on_write function was provided. + if (isset($options['write'])) { + $onWrite = $options['write']; + $buffer = FnStream::decorate($buffer, [ + 'write' => function ($string) use ($buffer, $onWrite) { + $result = $buffer->write($string); + $onWrite($buffer, $string); + return $result; + } + ]); + } + + return [$buffer, new self($buffer, $options)]; + } + + public function getSize() + { + return $this->size; + } + + public function isWritable() + { + return false; + } + + public function write($string) + { + return false; + } + + public function read($length) + { + if (!$this->needsDrain && $this->drain) { + $this->needsDrain = $this->stream->getSize() >= $this->hwm; + } + + $result = $this->stream->read($length); + + // If we need to drain, then drain when the buffer is empty. + if ($this->needsDrain && $this->stream->getSize() === 0) { + $this->needsDrain = false; + $drainFn = $this->drain; + $drainFn($this->stream); + } + + $resultLen = strlen($result); + + // If a pump was provided, the buffer is still open, and not enough + // data was given, then block until the data is provided. + if ($this->pump && $resultLen < $length) { + $pumpFn = $this->pump; + $result .= $pumpFn($length - $resultLen); + } + + return $result; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/BufferStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/BufferStream.php new file mode 100644 index 00000000..0fffbd63 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/BufferStream.php @@ -0,0 +1,138 @@ +hwm = $hwm; + } + + public function __toString() + { + return $this->getContents(); + } + + public function getContents() + { + $buffer = $this->buffer; + $this->buffer = ''; + + return $buffer; + } + + public function close() + { + $this->buffer = ''; + } + + public function detach() + { + $this->close(); + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function getSize() + { + return strlen($this->buffer); + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return true; + } + + public function isSeekable() + { + return false; + } + + public function seek($offset, $whence = SEEK_SET) + { + return false; + } + + public function eof() + { + return strlen($this->buffer) === 0; + } + + public function tell() + { + return false; + } + + /** + * Reads data from the buffer. + */ + public function read($length) + { + $currentLength = strlen($this->buffer); + + if ($length >= $currentLength) { + // No need to slice the buffer because we don't have enough data. + $result = $this->buffer; + $this->buffer = ''; + } else { + // Slice up the result to provide a subset of the buffer. + $result = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + } + + return $result; + } + + /** + * Writes data to the buffer. + */ + public function write($string) + { + $this->buffer .= $string; + + if (strlen($this->buffer) >= $this->hwm) { + return false; + } + + return strlen($string); + } + + public function getMetadata($key = null) + { + if ($key == 'hwm') { + return $this->hwm; + } + + return $key ? null : []; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/CachingStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/CachingStream.php new file mode 100644 index 00000000..60bb9056 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/CachingStream.php @@ -0,0 +1,122 @@ +remoteStream = $stream; + $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); + } + + public function getSize() + { + return max($this->stream->getSize(), $this->remoteStream->getSize()); + } + + /** + * {@inheritdoc} + * @throws SeekException When seeking with SEEK_END or when seeking + * past the total size of the buffer stream + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->tell(); + } else { + return false; + } + + // You cannot skip ahead past where you've read from the remote stream + if ($byte > $this->stream->getSize()) { + throw new SeekException( + $this, + $byte, + sprintf('Cannot seek to byte %d when the buffered stream only' + . ' contains %d bytes', $byte, $this->stream->getSize()) + ); + } + + return $this->stream->seek($byte); + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->stream->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have + // been filled from the remote stream, then we must skip bytes on + // the remote stream to emulate overwriting bytes from that + // position. This mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read( + $remaining + $this->skipReadBytes + ); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->stream->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want + // to skip bytes from being read from the remote stream to emulate + // other stream wrappers. Basically replacing bytes of data of a fixed + // length. + $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->stream->write($string); + } + + public function eof() + { + return $this->stream->eof() && $this->remoteStream->eof(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + $this->remoteStream->close() && $this->stream->close(); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/DroppingStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/DroppingStream.php new file mode 100644 index 00000000..56ee80c1 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/DroppingStream.php @@ -0,0 +1,42 @@ +stream = $stream; + $this->maxLength = $maxLength; + } + + public function write($string) + { + $diff = $this->maxLength - $this->stream->getSize(); + + // Begin returning false when the underlying stream is too large. + if ($diff <= 0) { + return false; + } + + // Write the stream or a subset of the stream if needed. + if (strlen($string) < $diff) { + return $this->stream->write($string); + } + + $this->stream->write(substr($string, 0, $diff)); + + return false; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php new file mode 100644 index 00000000..e631b9fa --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php @@ -0,0 +1,4 @@ +stream = $stream; + $msg = $msg ?: 'Could not seek the stream to position ' . $pos; + parent::__construct($msg); + } + + /** + * @return StreamInterface + */ + public function getStream() + { + return $this->stream; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/FnStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/FnStream.php new file mode 100644 index 00000000..6b5872d7 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/FnStream.php @@ -0,0 +1,147 @@ +methods = $methods; + + // Create the functions on the class + foreach ($methods as $name => $fn) { + $this->{'_fn_' . $name} = $fn; + } + } + + /** + * Lazily determine which methods are not implemented. + * @throws \BadMethodCallException + */ + public function __get($name) + { + throw new \BadMethodCallException(str_replace('_fn_', '', $name) + . '() is not implemented in the FnStream'); + } + + /** + * The close method is called on the underlying stream only if possible. + */ + public function __destruct() + { + if (isset($this->_fn_close)) { + call_user_func($this->_fn_close); + } + } + + /** + * Adds custom functionality to an underlying stream by intercepting + * specific method calls. + * + * @param StreamInterface $stream Stream to decorate + * @param array $methods Hash of method name to a closure + * + * @return FnStream + */ + public static function decorate(StreamInterface $stream, array $methods) + { + // If any of the required methods were not provided, then simply + // proxy to the decorated stream. + foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { + $methods[$diff] = [$stream, $diff]; + } + + return new self($methods); + } + + public function __toString() + { + return call_user_func($this->_fn___toString); + } + + public function close() + { + return call_user_func($this->_fn_close); + } + + public function detach() + { + return call_user_func($this->_fn_detach); + } + + public function attach($stream) + { + return call_user_func($this->_fn_attach, $stream); + } + + public function getSize() + { + return call_user_func($this->_fn_getSize); + } + + public function tell() + { + return call_user_func($this->_fn_tell); + } + + public function eof() + { + return call_user_func($this->_fn_eof); + } + + public function isSeekable() + { + return call_user_func($this->_fn_isSeekable); + } + + public function seek($offset, $whence = SEEK_SET) + { + return call_user_func($this->_fn_seek, $offset, $whence); + } + + public function isWritable() + { + return call_user_func($this->_fn_isWritable); + } + + public function write($string) + { + return call_user_func($this->_fn_write, $string); + } + + public function isReadable() + { + return call_user_func($this->_fn_isReadable); + } + + public function read($length) + { + return call_user_func($this->_fn_read, $length); + } + + public function getContents() + { + return call_user_func($this->_fn_getContents); + } + + public function getMetadata($key = null) + { + return call_user_func($this->_fn_getMetadata, $key); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php new file mode 100644 index 00000000..47ca01cd --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php @@ -0,0 +1,117 @@ +isReadable()) { + $mode = $stream->isWritable() ? 'r+' : 'r'; + } elseif ($stream->isWritable()) { + $mode = 'w'; + } else { + throw new \InvalidArgumentException('The stream must be readable, ' + . 'writable, or both.'); + } + + return fopen('guzzle://stream', $mode, null, stream_context_create([ + 'guzzle' => ['stream' => $stream], + ])); + } + + /** + * Registers the stream wrapper if needed + */ + public static function register() + { + if (!in_array('guzzle', stream_get_wrappers())) { + stream_wrapper_register('guzzle', __CLASS__); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) + { + $options = stream_context_get_options($this->context); + + if (!isset($options['guzzle']['stream'])) { + return false; + } + + $this->mode = $mode; + $this->stream = $options['guzzle']['stream']; + + return true; + } + + public function stream_read($count) + { + return $this->stream->read($count); + } + + public function stream_write($data) + { + return (int) $this->stream->write($data); + } + + public function stream_tell() + { + return $this->stream->tell(); + } + + public function stream_eof() + { + return $this->stream->eof(); + } + + public function stream_seek($offset, $whence) + { + return $this->stream->seek($offset, $whence); + } + + public function stream_stat() + { + static $modeMap = [ + 'r' => 33060, + 'r+' => 33206, + 'w' => 33188, + ]; + + return [ + 'dev' => 0, + 'ino' => 0, + 'mode' => $modeMap[$this->mode], + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => $this->stream->getSize() ?: 0, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0 + ]; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/InflateStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/InflateStream.php new file mode 100644 index 00000000..978af210 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/InflateStream.php @@ -0,0 +1,27 @@ +stream = new Stream($resource); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/LazyOpenStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/LazyOpenStream.php new file mode 100644 index 00000000..6242ee7b --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/LazyOpenStream.php @@ -0,0 +1,37 @@ +filename = $filename; + $this->mode = $mode; + } + + /** + * Creates the underlying stream lazily when required. + * + * @return StreamInterface + */ + protected function createStream() + { + return Stream::factory(Utils::open($this->filename, $this->mode)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/LimitStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/LimitStream.php new file mode 100644 index 00000000..e9fad985 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/LimitStream.php @@ -0,0 +1,161 @@ +stream = $stream; + $this->setLimit($limit); + $this->setOffset($offset); + } + + public function eof() + { + // Always return true if the underlying stream is EOF + if ($this->stream->eof()) { + return true; + } + + // No limit and the underlying stream is not at EOF + if ($this->limit == -1) { + return false; + } + + $tell = $this->stream->tell(); + if ($tell === false) { + return false; + } + + return $tell >= $this->offset + $this->limit; + } + + /** + * Returns the size of the limited subset of data + * {@inheritdoc} + */ + public function getSize() + { + if (null === ($length = $this->stream->getSize())) { + return null; + } elseif ($this->limit == -1) { + return $length - $this->offset; + } else { + return min($this->limit, $length - $this->offset); + } + } + + /** + * Allow for a bounded seek on the read limited stream + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence !== SEEK_SET || $offset < 0) { + return false; + } + + $offset += $this->offset; + + if ($this->limit !== -1) { + if ($offset > $this->offset + $this->limit) { + $offset = $this->offset + $this->limit; + } + } + + return $this->stream->seek($offset); + } + + /** + * Give a relative tell() + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell() - $this->offset; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @return self + * @throws SeekException + */ + public function setOffset($offset) + { + $current = $this->stream->tell(); + + if ($current !== $offset) { + // If the stream cannot seek to the offset position, then read to it + if (!$this->stream->seek($offset)) { + if ($current > $offset) { + throw new SeekException($this, $offset); + } else { + $this->stream->read($offset - $current); + } + } + } + + $this->offset = $offset; + + return $this; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the + * stream. + * + * @param int $limit Number of bytes to allow to be read from the stream. + * Use -1 for no limit. + * @return self + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + public function read($length) + { + if ($this->limit == -1) { + return $this->stream->read($length); + } + + // Check if the current position is less than the total allowed + // bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->stream->tell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte + // limit is not exceeded + return $this->stream->read(min($remaining, $length)); + } else { + return false; + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php new file mode 100644 index 00000000..c1433ad8 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php @@ -0,0 +1,11 @@ +stream->attach($stream); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/NullStream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/NullStream.php new file mode 100644 index 00000000..aeda6bec --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/NullStream.php @@ -0,0 +1,79 @@ +source = $source; + $this->size = isset($options['size']) ? $options['size'] : null; + $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; + $this->buffer = new BufferStream(); + } + + public function __toString() + { + return Utils::copyToString($this); + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->tellPos = false; + $this->source = null; + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function getSize() + { + return $this->size; + } + + public function tell() + { + return $this->tellPos; + } + + public function eof() + { + return !$this->source; + } + + public function isSeekable() + { + return false; + } + + public function seek($offset, $whence = SEEK_SET) + { + return false; + } + + public function isWritable() + { + return false; + } + + public function write($string) + { + return false; + } + + public function isReadable() + { + return true; + } + + public function read($length) + { + $data = $this->buffer->read($length); + $readLen = strlen($data); + $this->tellPos += $readLen; + $remaining = $length - $readLen; + + if ($remaining) { + $this->pump($remaining); + $data .= $this->buffer->read($remaining); + $this->tellPos += strlen($data) - $readLen; + } + + return $data; + } + + public function getContents() + { + $result = ''; + while (!$this->eof()) { + $result .= $this->read(1000000); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!$key) { + return $this->metadata; + } + + return isset($this->metadata[$key]) ? $this->metadata[$key] : null; + } + + private function pump($length) + { + if ($this->source) { + do { + $data = call_user_func($this->source, $length); + if ($data === false || $data === null) { + $this->source = null; + return; + } + $this->buffer->write($data); + $length -= strlen($data); + } while ($length > 0); + } + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/Stream.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/Stream.php new file mode 100644 index 00000000..81c80414 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/Stream.php @@ -0,0 +1,261 @@ + [ + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, + 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a+' => true, + ], + 'write' => [ + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, + 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, + 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true, + ], + ]; + + /** + * Create a new stream based on the input type. + * + * This factory accepts the same associative array of options as described + * in the constructor. + * + * @param resource|string|StreamInterface $resource Entity body data + * @param array $options Additional options + * + * @return Stream + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ + public static function factory($resource = '', array $options = []) + { + $type = gettype($resource); + + if ($type == 'string') { + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + return new self($stream, $options); + } + + if ($type == 'resource') { + return new self($resource, $options); + } + + if ($resource instanceof StreamInterface) { + return $resource; + } + + if ($type == 'object' && method_exists($resource, '__toString')) { + return self::factory((string) $resource, $options); + } + + if (is_callable($resource)) { + return new PumpStream($resource, $options); + } + + if ($resource instanceof \Iterator) { + return new PumpStream(function () use ($resource) { + if (!$resource->valid()) { + return false; + } + $result = $resource->current(); + $resource->next(); + return $result; + }, $options); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . $type); + } + + /** + * This constructor accepts an associative array of options. + * + * - size: (int) If a read stream would otherwise have an indeterminate + * size, but the size is known due to foreknownledge, then you can + * provide that size, in bytes. + * - metadata: (array) Any additional metadata to return when the metadata + * of the stream is accessed. + * + * @param resource $stream Stream resource to wrap. + * @param array $options Associative array of options. + * + * @throws \InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $options = []) + { + if (!is_resource($stream)) { + throw new \InvalidArgumentException('Stream must be a resource'); + } + + if (isset($options['size'])) { + $this->size = $options['size']; + } + + $this->customMetadata = isset($options['metadata']) + ? $options['metadata'] + : []; + + $this->attach($stream); + } + + /** + * Closes the stream when the destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + if (!$this->stream) { + return ''; + } + + $this->seek(0); + + return (string) stream_get_contents($this->stream); + } + + public function getContents() + { + return $this->stream ? stream_get_contents($this->stream) : ''; + } + + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + + $this->detach(); + } + + public function detach() + { + $result = $this->stream; + $this->stream = $this->size = $this->uri = null; + $this->readable = $this->writable = $this->seekable = false; + + return $result; + } + + public function attach($stream) + { + $this->stream = $stream; + $meta = stream_get_meta_data($this->stream); + $this->seekable = $meta['seekable']; + $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); + $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); + $this->uri = $this->getMetadata('uri'); + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + if (!$this->stream) { + return null; + } + + // Clear the stat cache if the stream has a URI + if ($this->uri) { + clearstatcache(true, $this->uri); + } + + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } + + return null; + } + + public function isReadable() + { + return $this->readable; + } + + public function isWritable() + { + return $this->writable; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function eof() + { + return !$this->stream || feof($this->stream); + } + + public function tell() + { + return $this->stream ? ftell($this->stream) : false; + } + + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->seekable + ? fseek($this->stream, $offset, $whence) === 0 + : false; + } + + public function read($length) + { + return $this->readable ? fread($this->stream, $length) : false; + } + + public function write($string) + { + // We can't know the size after writing anything + $this->size = null; + + return $this->writable ? fwrite($this->stream, $string) : false; + } + + public function getMetadata($key = null) + { + if (!$this->stream) { + return $key ? null : []; + } elseif (!$key) { + return $this->customMetadata + stream_get_meta_data($this->stream); + } elseif (isset($this->customMetadata[$key])) { + return $this->customMetadata[$key]; + } + + $meta = stream_get_meta_data($this->stream); + + return isset($meta[$key]) ? $meta[$key] : null; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php new file mode 100644 index 00000000..0f551d36 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php @@ -0,0 +1,144 @@ +stream = $stream; + } + + /** + * Magic method used to create a new stream if streams are not added in + * the constructor of a decorator (e.g., LazyOpenStream). + */ + public function __get($name) + { + if ($name == 'stream') { + $this->stream = $this->createStream(); + return $this->stream; + } + + throw new \UnexpectedValueException("$name not found on class"); + } + + public function __toString() + { + try { + $this->seek(0); + return $this->getContents(); + } catch (\Exception $e) { + // Really, PHP? https://bugs.php.net/bug.php?id=53648 + trigger_error('StreamDecorator::__toString exception: ' + . (string) $e, E_USER_ERROR); + return ''; + } + } + + public function getContents() + { + return Utils::copyToString($this); + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + $result = call_user_func_array(array($this->stream, $method), $args); + + // Always return the wrapped object if the result is a return $this + return $result === $this->stream ? $this : $result; + } + + public function close() + { + $this->stream->close(); + } + + public function getMetadata($key = null) + { + return $this->stream->getMetadata($key); + } + + public function detach() + { + return $this->stream->detach(); + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function getSize() + { + return $this->stream->getSize(); + } + + public function eof() + { + return $this->stream->eof(); + } + + public function tell() + { + return $this->stream->tell(); + } + + public function isReadable() + { + return $this->stream->isReadable(); + } + + public function isWritable() + { + return $this->stream->isWritable(); + } + + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->stream->seek($offset, $whence); + } + + public function read($length) + { + return $this->stream->read($length); + } + + public function write($string) + { + return $this->stream->write($string); + } + + /** + * Implement in subclasses to dynamically create streams when requested. + * + * @return StreamInterface + * @throws \BadMethodCallException + */ + protected function createStream() + { + throw new \BadMethodCallException('createStream() not implemented in ' + . get_class($this)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/src/StreamInterface.php b/lib/Ebanx/vendor/guzzlehttp/streams/src/StreamInterface.php new file mode 100644 index 00000000..fd19c6f2 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/src/StreamInterface.php @@ -0,0 +1,159 @@ +eof()) { + $buf = $stream->read(1048576); + if ($buf === false) { + break; + } + $buffer .= $buf; + } + return $buffer; + } + + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + if ($buf === false) { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + + return $buffer; + } + + /** + * Copy the contents of a stream into another stream until the given number + * of bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + */ + public static function copyToStream( + StreamInterface $source, + StreamInterface $dest, + $maxLen = -1 + ) { + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read(1048576))) { + break; + } + } + return; + } + + $bytes = 0; + while (!$source->eof()) { + $buf = $source->read($maxLen - $bytes); + if (!($len = strlen($buf))) { + break; + } + $bytes += $len; + $dest->write($buf); + if ($bytes == $maxLen) { + break; + } + } + } + + /** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return string Returns the hash of the stream + * @throws SeekException + */ + public static function hash( + StreamInterface $stream, + $algo, + $rawOutput = false + ) { + $pos = $stream->tell(); + + if ($pos > 0 && !$stream->seek(0)) { + throw new SeekException($stream); + } + + $ctx = hash_init($algo); + while (!$stream->eof()) { + hash_update($ctx, $stream->read(1048576)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; + } + + /** + * Read a line from the stream up to the maximum allowed buffer length + * + * @param StreamInterface $stream Stream to read from + * @param int $maxLength Maximum buffer length + * @param string $eol Line ending + * + * @return string|bool + */ + public static function readline(StreamInterface $stream, $maxLength = null, $eol = PHP_EOL) + { + $buffer = ''; + $size = 0; + $negEolLen = -strlen($eol); + + while (!$stream->eof()) { + if (false === ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if (++$size == $maxLength || substr($buffer, $negEolLen) === $eol) { + break; + } + } + + return $buffer; + } + + /** + * Alias of GuzzleHttp\Stream\Stream::factory. + * + * @param mixed $resource Resource to create + * @param array $options Associative array of stream options defined in + * {@see \GuzzleHttp\Stream\Stream::__construct} + * + * @return StreamInterface + * + * @see GuzzleHttp\Stream\Stream::factory + * @see GuzzleHttp\Stream\Stream::__construct + */ + public static function create($resource, array $options = []) + { + return Stream::factory($resource, $options); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/AppendStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/AppendStreamTest.php new file mode 100644 index 00000000..2a0adb61 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/AppendStreamTest.php @@ -0,0 +1,179 @@ +getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(false)); + $a->addStream($s); + } + + public function testValidatesSeekType() + { + $a = new AppendStream(); + $this->assertFalse($a->seek(100, SEEK_CUR)); + } + + public function testTriesToRewindOnSeek() + { + $a = new AppendStream(); + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable', 'seek', 'isSeekable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(true)); + $s->expects($this->once()) + ->method('seek') + ->will($this->returnValue(false)); + $a->addStream($s); + $this->assertFalse($a->seek(10)); + } + + public function testSeeksToPositionByReading() + { + $a = new AppendStream([ + Stream::factory('foo'), + Stream::factory('bar'), + Stream::factory('baz'), + ]); + + $this->assertTrue($a->seek(3)); + $this->assertEquals(3, $a->tell()); + $this->assertEquals('bar', $a->read(3)); + $a->seek(6); + $this->assertEquals(6, $a->tell()); + $this->assertEquals('baz', $a->read(3)); + } + + public function testDetachesEachStream() + { + $s1 = Stream::factory('foo'); + $s2 = Stream::factory('foo'); + $a = new AppendStream([$s1, $s2]); + $this->assertSame('foofoo', (string) $a); + $a->detach(); + $this->assertSame('', (string) $a); + $this->assertSame(0, $a->getSize()); + } + + public function testClosesEachStream() + { + $s1 = Stream::factory('foo'); + $a = new AppendStream([$s1]); + $a->close(); + $this->assertSame('', (string) $a); + } + + public function testIsNotWritable() + { + $a = new AppendStream([Stream::factory('foo')]); + $this->assertFalse($a->isWritable()); + $this->assertTrue($a->isSeekable()); + $this->assertTrue($a->isReadable()); + $this->assertFalse($a->write('foo')); + } + + public function testDoesNotNeedStreams() + { + $a = new AppendStream(); + $this->assertEquals('', (string) $a); + } + + public function testCanReadFromMultipleStreams() + { + $a = new AppendStream([ + Stream::factory('foo'), + Stream::factory('bar'), + Stream::factory('baz'), + ]); + $this->assertFalse($a->eof()); + $this->assertSame(0, $a->tell()); + $this->assertEquals('foo', $a->read(3)); + $this->assertEquals('bar', $a->read(3)); + $this->assertEquals('baz', $a->read(3)); + $this->assertEmpty($a->read(1)); + $this->assertTrue($a->eof()); + $this->assertSame(9, $a->tell()); + $this->assertEquals('foobarbaz', (string) $a); + } + + public function testCanDetermineSizeFromMultipleStreams() + { + $a = new AppendStream([ + Stream::factory('foo'), + Stream::factory('bar'), + ]); + $this->assertEquals(6, $a->getSize()); + + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable', 'isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(null)); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $a->addStream($s); + $this->assertNull($a->getSize()); + } + + public function testCatchesExceptionsWhenCastingToString() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['read', 'isReadable', 'eof']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('read') + ->will($this->throwException(new \RuntimeException('foo'))); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $s->expects($this->any()) + ->method('eof') + ->will($this->returnValue(false)); + $a = new AppendStream([$s]); + $this->assertFalse($a->eof()); + $this->assertSame('', (string) $a); + } + + public function testCanDetach() + { + $s = new AppendStream(); + $s->detach(); + } + + public function testReturnsEmptyMetadata() + { + $s = new AppendStream(); + $this->assertEquals([], $s->getMetadata()); + $this->assertNull($s->getMetadata('foo')); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = new AppendStream(); + $p->attach('a'); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php new file mode 100644 index 00000000..93ef9937 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php @@ -0,0 +1,186 @@ + function () { return false; }] + )); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Buffer must be readable and writable + */ + public function testValidatesWritableBuffer() + { + new AsyncReadStream(FnStream::decorate( + Stream::factory(), + ['isWritable' => function () { return false; }] + )); + } + + public function testValidatesHwmMetadata() + { + $a = new AsyncReadStream(Stream::factory(), [ + 'drain' => function() {} + ]); + $this->assertNull($this->readAttribute($a, 'drain')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage pump must be callable + */ + public function testValidatesPumpIsCallable() + { + new AsyncReadStream(new BufferStream(), ['pump' => true]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage drain must be callable + */ + public function testValidatesDrainIsCallable() + { + new AsyncReadStream(new BufferStream(), ['drain' => true]); + } + + public function testCanInitialize() + { + $buffer = new BufferStream(); + $a = new AsyncReadStream($buffer, [ + 'size' => 10, + 'drain' => function () {}, + 'pump' => function () {}, + ]); + $this->assertSame($buffer, $this->readAttribute($a, 'stream')); + $this->assertTrue(is_callable($this->readAttribute($a, 'drain'))); + $this->assertTrue(is_callable($this->readAttribute($a, 'pump'))); + $this->assertTrue($a->isReadable()); + $this->assertFalse($a->isSeekable()); + $this->assertFalse($a->isWritable()); + $this->assertFalse($a->write('foo')); + $this->assertEquals(10, $a->getSize()); + } + + public function testReadsFromBufferWithNoDrainOrPump() + { + $buffer = new BufferStream(); + $a = new AsyncReadStream($buffer); + $buffer->write('foo'); + $this->assertNull($a->getSize()); + $this->assertEquals('foo', $a->read(10)); + $this->assertEquals('', $a->read(10)); + } + + public function testCallsPumpForMoreDataWhenRequested() + { + $called = 0; + $buffer = new BufferStream(); + $a = new AsyncReadStream($buffer, [ + 'pump' => function ($size) use (&$called) { + $called++; + return str_repeat('.', $size); + } + ]); + $buffer->write('foobar'); + $this->assertEquals('foo', $a->read(3)); + $this->assertEquals(0, $called); + $this->assertEquals('bar.....', $a->read(8)); + $this->assertEquals(1, $called); + $this->assertEquals('..', $a->read(2)); + $this->assertEquals(2, $called); + } + + public function testCallsDrainWhenNeeded() + { + $called = 0; + $buffer = new BufferStream(5); + $a = new AsyncReadStream($buffer, [ + 'drain' => function (BufferStream $b) use (&$called, $buffer) { + $this->assertSame($b, $buffer); + $called++; + } + ]); + + $buffer->write('foobar'); + $this->assertEquals(6, $buffer->getSize()); + $this->assertEquals(0, $called); + + $a->read(3); + $this->assertTrue($this->readAttribute($a, 'needsDrain')); + $this->assertEquals(3, $buffer->getSize()); + $this->assertEquals(0, $called); + + $a->read(3); + $this->assertEquals(0, $buffer->getSize()); + $this->assertFalse($this->readAttribute($a, 'needsDrain')); + $this->assertEquals(1, $called); + } + + public function testCreatesBufferWithNoConfig() + { + list($buffer, $async) = AsyncReadStream::create(); + $this->assertInstanceOf('GuzzleHttp\Stream\BufferStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + } + + public function testCreatesBufferWithSpecifiedBuffer() + { + $buf = new BufferStream(); + list($buffer, $async) = AsyncReadStream::create(['buffer' => $buf]); + $this->assertSame($buf, $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + } + + public function testCreatesNullStream() + { + list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 0]); + $this->assertInstanceOf('GuzzleHttp\Stream\NullStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + } + + public function testCreatesDroppingStream() + { + list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 5]); + $this->assertInstanceOf('GuzzleHttp\Stream\DroppingStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + $buffer->write('12345678910'); + $this->assertEquals(5, $buffer->getSize()); + } + + public function testCreatesOnWriteStream() + { + $c = 0; + $b = new BufferStream(); + list($buffer, $async) = AsyncReadStream::create([ + 'buffer' => $b, + 'write' => function (BufferStream $buf, $data) use (&$c, $b) { + $this->assertSame($buf, $b); + $this->assertEquals('foo', $data); + $c++; + }, + ]); + $this->assertInstanceOf('GuzzleHttp\Stream\FnStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + $this->assertEquals(0, $c); + $this->assertEquals(3, $buffer->write('foo')); + $this->assertEquals(1, $c); + $this->assertEquals(3, $buffer->write('foo')); + $this->assertEquals(2, $c); + $this->assertEquals('foofoo', (string) $buffer); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/BufferStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/BufferStreamTest.php new file mode 100644 index 00000000..f9bfea21 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/BufferStreamTest.php @@ -0,0 +1,69 @@ +assertTrue($b->isReadable()); + $this->assertTrue($b->isWritable()); + $this->assertFalse($b->isSeekable()); + $this->assertEquals(null, $b->getMetadata('foo')); + $this->assertEquals(10, $b->getMetadata('hwm')); + $this->assertEquals([], $b->getMetadata()); + } + + public function testRemovesReadDataFromBuffer() + { + $b = new BufferStream(); + $this->assertEquals(3, $b->write('foo')); + $this->assertEquals(3, $b->getSize()); + $this->assertFalse($b->eof()); + $this->assertEquals('foo', $b->read(10)); + $this->assertTrue($b->eof()); + $this->assertEquals('', $b->read(10)); + } + + public function testCanCastToStringOrGetContents() + { + $b = new BufferStream(); + $b->write('foo'); + $b->write('baz'); + $this->assertEquals('foo', $b->read(3)); + $b->write('bar'); + $this->assertEquals('bazbar', (string) $b); + $this->assertFalse($b->tell()); + } + + public function testDetachClearsBuffer() + { + $b = new BufferStream(); + $b->write('foo'); + $b->detach(); + $this->assertEquals(0, $b->tell()); + $this->assertTrue($b->eof()); + $this->assertEquals(3, $b->write('abc')); + $this->assertEquals('abc', $b->read(10)); + } + + public function testExceedingHighwaterMarkReturnsFalseButStillBuffers() + { + $b = new BufferStream(5); + $this->assertEquals(3, $b->write('hi ')); + $this->assertFalse($b->write('hello')); + $this->assertEquals('hi hello', (string) $b); + $this->assertEquals(4, $b->write('test')); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = new BufferStream(); + $p->attach('a'); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/CachingStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/CachingStreamTest.php new file mode 100644 index 00000000..ea969b3a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/CachingStreamTest.php @@ -0,0 +1,136 @@ +decorated = Stream::factory('testing'); + $this->body = new CachingStream($this->decorated); + } + + public function tearDown() + { + $this->decorated->close(); + $this->body->close(); + } + + public function testUsesRemoteSizeIfPossible() + { + $body = Stream::factory('test'); + $caching = new CachingStream($body); + $this->assertEquals(4, $caching->getSize()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot seek to byte 10 + */ + public function testCannotSeekPastWhatHasBeenRead() + { + $this->body->seek(10); + } + + public function testCannotUseSeekEnd() + { + $this->assertFalse($this->body->seek(2, SEEK_END)); + } + + public function testRewindUsesSeek() + { + $a = Stream::factory('foo'); + $d = $this->getMockBuilder('GuzzleHttp\Stream\CachingStream') + ->setMethods(array('seek')) + ->setConstructorArgs(array($a)) + ->getMock(); + $d->expects($this->once()) + ->method('seek') + ->with(0) + ->will($this->returnValue(true)); + $d->seek(0); + } + + public function testCanSeekToReadBytes() + { + $this->assertEquals('te', $this->body->read(2)); + $this->body->seek(0); + $this->assertEquals('test', $this->body->read(4)); + $this->assertEquals(4, $this->body->tell()); + $this->body->seek(2); + $this->assertEquals(2, $this->body->tell()); + $this->body->seek(2, SEEK_CUR); + $this->assertEquals(4, $this->body->tell()); + $this->assertEquals('ing', $this->body->read(3)); + } + + public function testWritesToBufferStream() + { + $this->body->read(2); + $this->body->write('hi'); + $this->body->seek(0); + $this->assertEquals('tehiing', (string) $this->body); + } + + public function testSkipsOverwrittenBytes() + { + $decorated = Stream::factory( + implode("\n", array_map(function ($n) { + return str_pad($n, 4, '0', STR_PAD_LEFT); + }, range(0, 25))) + ); + + $body = new CachingStream($decorated); + + $this->assertEquals("0000\n", Utils::readline($body)); + $this->assertEquals("0001\n", Utils::readline($body)); + // Write over part of the body yet to be read, so skip some bytes + $this->assertEquals(5, $body->write("TEST\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + // Read, which skips bytes, then reads + $this->assertEquals("0003\n", Utils::readline($body)); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("0004\n", Utils::readline($body)); + $this->assertEquals("0005\n", Utils::readline($body)); + + // Overwrite part of the cached body (so don't skip any bytes) + $body->seek(5); + $this->assertEquals(5, $body->write("ABCD\n")); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("TEST\n", Utils::readline($body)); + $this->assertEquals("0003\n", Utils::readline($body)); + $this->assertEquals("0004\n", Utils::readline($body)); + $this->assertEquals("0005\n", Utils::readline($body)); + $this->assertEquals("0006\n", Utils::readline($body)); + $this->assertEquals(5, $body->write("1234\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + + // Seek to 0 and ensure the overwritten bit is replaced + $body->seek(0); + $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); + + // Ensure that casting it to a string does not include the bit that was overwritten + $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); + } + + public function testClosesBothStreams() + { + $s = fopen('php://temp', 'r'); + $a = Stream::factory($s); + $d = new CachingStream($a); + $d->close(); + $this->assertFalse(is_resource($s)); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php new file mode 100644 index 00000000..bb2cb220 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php @@ -0,0 +1,26 @@ +assertEquals(3, $drop->write('hel')); + $this->assertFalse($drop->write('lo')); + $this->assertEquals(5, $drop->getSize()); + $this->assertEquals('hello', $drop->read(5)); + $this->assertEquals(0, $drop->getSize()); + $drop->write('12345678910'); + $this->assertEquals(5, $stream->getSize()); + $this->assertEquals(5, $drop->getSize()); + $this->assertEquals('12345', (string) $drop); + $this->assertEquals(0, $drop->getSize()); + $drop->write('hello'); + $this->assertFalse($drop->write('test')); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php new file mode 100644 index 00000000..fd8cd1ad --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php @@ -0,0 +1,16 @@ +assertSame($s, $e->getStream()); + $this->assertContains('10', $e->getMessage()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/FnStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/FnStreamTest.php new file mode 100644 index 00000000..0597a7af --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/FnStreamTest.php @@ -0,0 +1,90 @@ +seek(1); + } + + public function testProxiesToFunction() + { + $s = new FnStream([ + 'read' => function ($len) { + $this->assertEquals(3, $len); + return 'foo'; + }, + ]); + + $this->assertEquals('foo', $s->read(3)); + } + + public function testCanCloseOnDestruct() + { + $called = false; + $s = new FnStream([ + 'close' => function () use (&$called) { + $called = true; + }, + ]); + unset($s); + $this->assertTrue($called); + } + + public function testDoesNotRequireClose() + { + $s = new FnStream([]); + unset($s); + } + + public function testDecoratesStream() + { + $a = Stream::factory('foo'); + $b = FnStream::decorate($a, []); + $this->assertEquals(3, $b->getSize()); + $this->assertEquals($b->isWritable(), true); + $this->assertEquals($b->isReadable(), true); + $this->assertEquals($b->isSeekable(), true); + $this->assertEquals($b->read(3), 'foo'); + $this->assertEquals($b->tell(), 3); + $this->assertEquals($a->tell(), 3); + $this->assertEmpty($b->read(1)); + $this->assertEquals($b->eof(), true); + $this->assertEquals($a->eof(), true); + $b->seek(0); + $this->assertEquals('foo', (string) $b); + $b->seek(0); + $this->assertEquals('foo', $b->getContents()); + $this->assertEquals($a->getMetadata(), $b->getMetadata()); + $b->seek(0, SEEK_END); + $b->write('bar'); + $this->assertEquals('foobar', (string) $b); + $this->assertInternalType('resource', $b->detach()); + $b->close(); + } + + public function testDecoratesWithCustomizations() + { + $called = false; + $a = Stream::factory('foo'); + $b = FnStream::decorate($a, [ + 'read' => function ($len) use (&$called, $a) { + $called = true; + return $a->read($len); + } + ]); + $this->assertEquals('foo', $b->read(3)); + $this->assertTrue($called); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php new file mode 100644 index 00000000..f10cc783 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php @@ -0,0 +1,100 @@ +assertSame('foo', fread($handle, 3)); + $this->assertSame(3, ftell($handle)); + $this->assertSame(3, fwrite($handle, 'bar')); + $this->assertSame(0, fseek($handle, 0)); + $this->assertSame('foobar', fread($handle, 6)); + $this->assertEmpty(fread($handle, 1)); + $this->assertTrue(feof($handle)); + + // This fails on HHVM for some reason + if (!defined('HHVM_VERSION')) { + $this->assertEquals([ + 'dev' => 0, + 'ino' => 0, + 'mode' => 33206, + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => 6, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0, + 0 => 0, + 1 => 0, + 2 => 33206, + 3 => 0, + 4 => 0, + 5 => 0, + 6 => 0, + 7 => 6, + 8 => 0, + 9 => 0, + 10 => 0, + 11 => 0, + 12 => 0, + ], fstat($handle)); + } + + $this->assertTrue(fclose($handle)); + $this->assertSame('foobar', (string) $stream); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesStream() + { + $stream = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable', 'isWritable']) + ->getMockForAbstractClass(); + $stream->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(false)); + $stream->expects($this->once()) + ->method('isWritable') + ->will($this->returnValue(false)); + GuzzleStreamWrapper::getResource($stream); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Warning + */ + public function testReturnsFalseWhenStreamDoesNotExist() + { + fopen('guzzle://foo', 'r'); + } + + public function testCanOpenReadonlyStream() + { + $stream = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable', 'isWritable']) + ->getMockForAbstractClass(); + $stream->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(false)); + $stream->expects($this->once()) + ->method('isWritable') + ->will($this->returnValue(true)); + $r = GuzzleStreamWrapper::getResource($stream); + $this->assertInternalType('resource', $r); + fclose($r); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/InflateStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/InflateStreamTest.php new file mode 100644 index 00000000..ead9356a --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/InflateStreamTest.php @@ -0,0 +1,16 @@ +assertEquals('test', (string) $b); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php new file mode 100644 index 00000000..79e0078e --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php @@ -0,0 +1,64 @@ +fname = tempnam('/tmp', 'tfile'); + + if (file_exists($this->fname)) { + unlink($this->fname); + } + } + + public function tearDown() + { + if (file_exists($this->fname)) { + unlink($this->fname); + } + } + + public function testOpensLazily() + { + $l = new LazyOpenStream($this->fname, 'w+'); + $l->write('foo'); + $this->assertInternalType('array', $l->getMetadata()); + $this->assertFileExists($this->fname); + $this->assertEquals('foo', file_get_contents($this->fname)); + $this->assertEquals('foo', (string) $l); + } + + public function testProxiesToFile() + { + file_put_contents($this->fname, 'foo'); + $l = new LazyOpenStream($this->fname, 'r'); + $this->assertEquals('foo', $l->read(4)); + $this->assertTrue($l->eof()); + $this->assertEquals(3, $l->tell()); + $this->assertTrue($l->isReadable()); + $this->assertTrue($l->isSeekable()); + $this->assertFalse($l->isWritable()); + $l->seek(1); + $this->assertEquals('oo', $l->getContents()); + $this->assertEquals('foo', (string) $l); + $this->assertEquals(3, $l->getSize()); + $this->assertInternalType('array', $l->getMetadata()); + $l->close(); + } + + public function testDetachesUnderlyingStream() + { + file_put_contents($this->fname, 'foo'); + $l = new LazyOpenStream($this->fname, 'r'); + $r = $l->detach(); + $this->assertInternalType('resource', $r); + fseek($r, 0); + $this->assertEquals('foo', stream_get_contents($r)); + fclose($r); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/LimitStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/LimitStreamTest.php new file mode 100644 index 00000000..6137e82d --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/LimitStreamTest.php @@ -0,0 +1,134 @@ +decorated = Stream::factory(fopen(__FILE__, 'r')); + $this->body = new LimitStream($this->decorated, 10, 3); + } + + public function testReturnsSubset() + { + $body = new LimitStream(Stream::factory('foo'), -1, 1); + $this->assertEquals('oo', (string) $body); + $this->assertTrue($body->eof()); + $body->seek(0); + $this->assertFalse($body->eof()); + $this->assertEquals('oo', $body->read(100)); + $this->assertEmpty($body->read(1)); + $this->assertTrue($body->eof()); + } + + public function testReturnsSubsetWhenCastToString() + { + $body = Stream::factory('foo_baz_bar'); + $limited = new LimitStream($body, 3, 4); + $this->assertEquals('baz', (string) $limited); + } + + public function testReturnsSubsetOfEmptyBodyWhenCastToString() + { + $body = Stream::factory(''); + $limited = new LimitStream($body, 0, 10); + $this->assertEquals('', (string) $limited); + } + + public function testSeeksWhenConstructed() + { + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + } + + public function testAllowsBoundedSeek() + { + $this->assertEquals(true, $this->body->seek(100)); + $this->assertEquals(10, $this->body->tell()); + $this->assertEquals(13, $this->decorated->tell()); + $this->assertEquals(true, $this->body->seek(0)); + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + $this->assertEquals(false, $this->body->seek(-10)); + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + $this->assertEquals(true, $this->body->seek(5)); + $this->assertEquals(5, $this->body->tell()); + $this->assertEquals(8, $this->decorated->tell()); + $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); + } + + public function testReadsOnlySubsetOfData() + { + $data = $this->body->read(100); + $this->assertEquals(10, strlen($data)); + $this->assertFalse($this->body->read(1000)); + + $this->body->setOffset(10); + $newData = $this->body->read(100); + $this->assertEquals(10, strlen($newData)); + $this->assertNotSame($data, $newData); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\SeekException + * @expectedExceptionMessage Could not seek the stream to position 2 + */ + public function testThrowsWhenCurrentGreaterThanOffsetSeek() + { + $a = Stream::factory('foo_bar'); + $b = new NoSeekStream($a); + $c = new LimitStream($b); + $a->getContents(); + $c->setOffset(2); + } + + public function testClaimsConsumedWhenReadLimitIsReached() + { + $this->assertFalse($this->body->eof()); + $this->body->read(1000); + $this->assertTrue($this->body->eof()); + } + + public function testContentLengthIsBounded() + { + $this->assertEquals(10, $this->body->getSize()); + } + + public function testGetContentsIsBasedOnSubset() + { + $body = new LimitStream(Stream::factory('foobazbar'), 3, 3); + $this->assertEquals('baz', $body->getContents()); + } + + public function testReturnsNullIfSizeCannotBeDetermined() + { + $a = new FnStream([ + 'getSize' => function () { return null; }, + 'tell' => function () { return 0; }, + ]); + $b = new LimitStream($a); + $this->assertNull($b->getSize()); + } + + public function testLengthLessOffsetWhenNoLimitSize() + { + $a = Stream::factory('foo_bar'); + $b = new LimitStream($a, -1, 4); + $this->assertEquals(3, $b->getSize()); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php new file mode 100644 index 00000000..21b7c6d2 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php @@ -0,0 +1,41 @@ +getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable', 'seek']) + ->getMockForAbstractClass(); + $s->expects($this->never())->method('seek'); + $s->expects($this->never())->method('isSeekable'); + $wrapped = new NoSeekStream($s); + $this->assertFalse($wrapped->isSeekable()); + $this->assertFalse($wrapped->seek(2)); + } + + public function testHandlesClose() + { + $s = Stream::factory('foo'); + $wrapped = new NoSeekStream($s); + $wrapped->close(); + $this->assertFalse($wrapped->write('foo')); + } + + public function testCanAttach() + { + $s1 = Stream::factory('foo'); + $s2 = Stream::factory('bar'); + $wrapped = new NoSeekStream($s1); + $wrapped->attach($s2->detach()); + $this->assertEquals('bar', (string) $wrapped); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/NullStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/NullStreamTest.php new file mode 100644 index 00000000..8e414315 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/NullStreamTest.php @@ -0,0 +1,39 @@ +assertEquals('', $b->read(10)); + $this->assertEquals(4, $b->write('test')); + $this->assertEquals('', (string) $b); + $this->assertNull($b->getMetadata('a')); + $this->assertEquals([], $b->getMetadata()); + $this->assertEquals(0, $b->getSize()); + $this->assertEquals('', $b->getContents()); + $this->assertEquals(0, $b->tell()); + + $this->assertTrue($b->isReadable()); + $this->assertTrue($b->isWritable()); + $this->assertTrue($b->isSeekable()); + $this->assertFalse($b->seek(10)); + + $this->assertTrue($b->eof()); + $b->detach(); + $this->assertTrue($b->eof()); + $b->close(); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = new NullStream(); + $p->attach('a'); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/PumpStreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/PumpStreamTest.php new file mode 100644 index 00000000..2d20ce90 --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/PumpStreamTest.php @@ -0,0 +1,77 @@ + ['foo' => 'bar'], + 'size' => 100 + ]); + + $this->assertEquals('bar', $p->getMetadata('foo')); + $this->assertEquals(['foo' => 'bar'], $p->getMetadata()); + $this->assertEquals(100, $p->getSize()); + } + + public function testCanReadFromCallable() + { + $p = Stream::factory(function ($size) { + return 'a'; + }); + $this->assertEquals('a', $p->read(1)); + $this->assertEquals(1, $p->tell()); + $this->assertEquals('aaaaa', $p->read(5)); + $this->assertEquals(6, $p->tell()); + } + + public function testStoresExcessDataInBuffer() + { + $called = []; + $p = Stream::factory(function ($size) use (&$called) { + $called[] = $size; + return 'abcdef'; + }); + $this->assertEquals('a', $p->read(1)); + $this->assertEquals('b', $p->read(1)); + $this->assertEquals('cdef', $p->read(4)); + $this->assertEquals('abcdefabc', $p->read(9)); + $this->assertEquals([1, 9, 3], $called); + } + + public function testInifiniteStreamWrappedInLimitStream() + { + $p = Stream::factory(function () { return 'a'; }); + $s = new LimitStream($p, 5); + $this->assertEquals('aaaaa', (string) $s); + } + + public function testDescribesCapabilities() + { + $p = Stream::factory(function () {}); + $this->assertTrue($p->isReadable()); + $this->assertFalse($p->isSeekable()); + $this->assertFalse($p->isWritable()); + $this->assertNull($p->getSize()); + $this->assertFalse($p->write('aa')); + $this->assertEquals('', $p->getContents()); + $this->assertEquals('', (string) $p); + $p->close(); + $this->assertEquals('', $p->read(10)); + $this->assertTrue($p->eof()); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = Stream::factory(function () {}); + $p->attach('a'); + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php new file mode 100644 index 00000000..2ba79add --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php @@ -0,0 +1,147 @@ +c = fopen('php://temp', 'r+'); + fwrite($this->c, 'foo'); + fseek($this->c, 0); + $this->a = Stream::factory($this->c); + $this->b = new Str($this->a); + } + + public function testCatchesExceptionsWhenCastingToString() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['read']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('read') + ->will($this->throwException(new \Exception('foo'))); + $msg = ''; + set_error_handler(function ($errNo, $str) use (&$msg) { $msg = $str; }); + echo new Str($s); + restore_error_handler(); + $this->assertContains('foo', $msg); + } + + public function testToString() + { + $this->assertEquals('foo', (string) $this->b); + } + + public function testHasSize() + { + $this->assertEquals(3, $this->b->getSize()); + $this->assertSame($this->b, $this->b->setSize(2)); + $this->assertEquals(2, $this->b->getSize()); + } + + public function testReads() + { + $this->assertEquals('foo', $this->b->read(10)); + } + + public function testCheckMethods() + { + $this->assertEquals($this->a->isReadable(), $this->b->isReadable()); + $this->assertEquals($this->a->isWritable(), $this->b->isWritable()); + $this->assertEquals($this->a->isSeekable(), $this->b->isSeekable()); + } + + public function testSeeksAndTells() + { + $this->assertTrue($this->b->seek(1)); + $this->assertEquals(1, $this->a->tell()); + $this->assertEquals(1, $this->b->tell()); + $this->assertTrue($this->b->seek(0)); + $this->assertEquals(0, $this->a->tell()); + $this->assertEquals(0, $this->b->tell()); + $this->assertTrue($this->b->seek(0, SEEK_END)); + $this->assertEquals(3, $this->a->tell()); + $this->assertEquals(3, $this->b->tell()); + } + + public function testGetsContents() + { + $this->assertEquals('foo', $this->b->getContents()); + $this->assertEquals('', $this->b->getContents()); + $this->b->seek(1); + $this->assertEquals('oo', $this->b->getContents(1)); + } + + public function testCloses() + { + $this->b->close(); + $this->assertFalse(is_resource($this->c)); + } + + public function testDetaches() + { + $this->b->detach(); + $this->assertFalse($this->b->isReadable()); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttachByDefault() + { + $this->b->attach('a'); + } + + public function testWrapsMetadata() + { + $this->assertSame($this->b->getMetadata(), $this->a->getMetadata()); + $this->assertSame($this->b->getMetadata('uri'), $this->a->getMetadata('uri')); + } + + public function testWrapsWrites() + { + $this->b->seek(0, SEEK_END); + $this->b->write('foo'); + $this->assertEquals('foofoo', (string) $this->a); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testThrowsWithInvalidGetter() + { + $this->b->foo; + } + + /** + * @expectedException \BadMethodCallException + */ + public function testThrowsWhenGetterNotImplemented() + { + $s = new BadStream(); + $s->stream; + } +} + +class BadStream +{ + use StreamDecoratorTrait; + + public function __construct() {} +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/StreamTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/StreamTest.php new file mode 100644 index 00000000..2985bfbb --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/StreamTest.php @@ -0,0 +1,252 @@ +assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isSeekable()); + $this->assertEquals('php://temp', $stream->getMetadata('uri')); + $this->assertInternalType('array', $stream->getMetadata()); + $this->assertEquals(4, $stream->getSize()); + $this->assertFalse($stream->eof()); + $stream->close(); + } + + public function testStreamClosesHandleOnDestruct() + { + $handle = fopen('php://temp', 'r'); + $stream = new Stream($handle); + unset($stream); + $this->assertFalse(is_resource($handle)); + } + + public function testConvertsToString() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('data', (string) $stream); + $this->assertEquals('data', (string) $stream); + $stream->close(); + } + + public function testGetsContents() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('', $stream->getContents()); + $stream->seek(0); + $this->assertEquals('data', $stream->getContents()); + $this->assertEquals('', $stream->getContents()); + } + + public function testChecksEof() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertFalse($stream->eof()); + $stream->read(4); + $this->assertTrue($stream->eof()); + $stream->close(); + } + + public function testAllowsSettingManualSize() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $stream->setSize(10); + $this->assertEquals(10, $stream->getSize()); + $stream->close(); + } + + public function testGetSize() + { + $size = filesize(__FILE__); + $handle = fopen(__FILE__, 'r'); + $stream = new Stream($handle); + $this->assertEquals($size, $stream->getSize()); + // Load from cache + $this->assertEquals($size, $stream->getSize()); + $stream->close(); + } + + public function testEnsuresSizeIsConsistent() + { + $h = fopen('php://temp', 'w+'); + $this->assertEquals(3, fwrite($h, 'foo')); + $stream = new Stream($h); + $this->assertEquals(3, $stream->getSize()); + $this->assertEquals(4, $stream->write('test')); + $this->assertEquals(7, $stream->getSize()); + $this->assertEquals(7, $stream->getSize()); + $stream->close(); + } + + public function testProvidesStreamPosition() + { + $handle = fopen('php://temp', 'w+'); + $stream = new Stream($handle); + $this->assertEquals(0, $stream->tell()); + $stream->write('foo'); + $this->assertEquals(3, $stream->tell()); + $stream->seek(1); + $this->assertEquals(1, $stream->tell()); + $this->assertSame(ftell($handle), $stream->tell()); + $stream->close(); + } + + public function testKeepsPositionOfResource() + { + $h = fopen(__FILE__, 'r'); + fseek($h, 10); + $stream = Stream::factory($h); + $this->assertEquals(10, $stream->tell()); + $stream->close(); + } + + public function testCanDetachAndAttachStream() + { + $r = fopen('php://temp', 'w+'); + $stream = new Stream($r); + $stream->write('foo'); + $this->assertTrue($stream->isReadable()); + $this->assertSame($r, $stream->detach()); + $this->assertNull($stream->detach()); + + $this->assertFalse($stream->isReadable()); + $this->assertFalse($stream->read(10)); + $this->assertFalse($stream->isWritable()); + $this->assertFalse($stream->write('bar')); + $this->assertFalse($stream->isSeekable()); + $this->assertFalse($stream->seek(10)); + $this->assertFalse($stream->tell()); + $this->assertTrue($stream->eof()); + $this->assertNull($stream->getSize()); + $this->assertSame('', (string) $stream); + $this->assertSame('', $stream->getContents()); + + $stream->attach($r); + $stream->seek(0); + $this->assertEquals('foo', $stream->getContents()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isSeekable()); + + $stream->close(); + } + + public function testCloseClearProperties() + { + $handle = fopen('php://temp', 'r+'); + $stream = new Stream($handle); + $stream->close(); + + $this->assertEmpty($stream->getMetadata()); + $this->assertFalse($stream->isSeekable()); + $this->assertFalse($stream->isReadable()); + $this->assertFalse($stream->isWritable()); + $this->assertNull($stream->getSize()); + } + + public function testCreatesWithFactory() + { + $stream = Stream::factory('foo'); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $stream); + $this->assertEquals('foo', $stream->getContents()); + $stream->close(); + } + + public function testFactoryCreatesFromEmptyString() + { + $s = Stream::factory(); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + } + + public function testFactoryCreatesFromResource() + { + $r = fopen(__FILE__, 'r'); + $s = Stream::factory($r); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + $this->assertSame(file_get_contents(__FILE__), (string) $s); + } + + public function testFactoryCreatesFromObjectWithToString() + { + $r = new HasToString(); + $s = Stream::factory($r); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + $this->assertEquals('foo', (string) $s); + } + + public function testCreatePassesThrough() + { + $s = Stream::factory('foo'); + $this->assertSame($s, Stream::factory($s)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionForUnknown() + { + Stream::factory(new \stdClass()); + } + + public function testReturnsCustomMetadata() + { + $s = Stream::factory('foo', ['metadata' => ['hwm' => 3]]); + $this->assertEquals(3, $s->getMetadata('hwm')); + $this->assertArrayHasKey('hwm', $s->getMetadata()); + } + + public function testCanSetSize() + { + $s = Stream::factory('', ['size' => 10]); + $this->assertEquals(10, $s->getSize()); + } + + public function testCanCreateIteratorBasedStream() + { + $a = new \ArrayIterator(['foo', 'bar', '123']); + $p = Stream::factory($a); + $this->assertInstanceOf('GuzzleHttp\Stream\PumpStream', $p); + $this->assertEquals('foo', $p->read(3)); + $this->assertFalse($p->eof()); + $this->assertEquals('b', $p->read(1)); + $this->assertEquals('a', $p->read(1)); + $this->assertEquals('r12', $p->read(3)); + $this->assertFalse($p->eof()); + $this->assertEquals('3', $p->getContents()); + $this->assertTrue($p->eof()); + $this->assertEquals(9, $p->tell()); + } +} + +class HasToString +{ + public function __toString() { + return 'foo'; + } +} diff --git a/lib/Ebanx/vendor/guzzlehttp/streams/tests/UtilsTest.php b/lib/Ebanx/vendor/guzzlehttp/streams/tests/UtilsTest.php new file mode 100644 index 00000000..6cf653cc --- /dev/null +++ b/lib/Ebanx/vendor/guzzlehttp/streams/tests/UtilsTest.php @@ -0,0 +1,162 @@ +assertEquals('foobaz', Utils::copyToString($s)); + $s->seek(0); + $this->assertEquals('foo', Utils::copyToString($s, 3)); + $this->assertEquals('baz', Utils::copyToString($s, 3)); + $this->assertEquals('', Utils::copyToString($s)); + } + + public function testCopiesToStringStopsWhenReadFails() + { + $s1 = Stream::factory('foobaz'); + $s1 = FnStream::decorate($s1, [ + 'read' => function () { + return false; + } + ]); + $result = Utils::copyToString($s1); + $this->assertEquals('', $result); + } + + public function testCopiesToStream() + { + $s1 = Stream::factory('foobaz'); + $s2 = Stream::factory(''); + Utils::copyToStream($s1, $s2); + $this->assertEquals('foobaz', (string) $s2); + $s2 = Stream::factory(''); + $s1->seek(0); + Utils::copyToStream($s1, $s2, 3); + $this->assertEquals('foo', (string) $s2); + Utils::copyToStream($s1, $s2, 3); + $this->assertEquals('foobaz', (string) $s2); + } + + public function testStopsCopyToStreamWhenWriteFails() + { + $s1 = Stream::factory('foobaz'); + $s2 = Stream::factory(''); + $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); + Utils::copyToStream($s1, $s2); + $this->assertEquals('', (string) $s2); + } + + public function testStopsCopyToSteamWhenWriteFailsWithMaxLen() + { + $s1 = Stream::factory('foobaz'); + $s2 = Stream::factory(''); + $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); + Utils::copyToStream($s1, $s2, 10); + $this->assertEquals('', (string) $s2); + } + + public function testStopsCopyToSteamWhenReadFailsWithMaxLen() + { + $s1 = Stream::factory('foobaz'); + $s1 = FnStream::decorate($s1, ['read' => function () { return ''; }]); + $s2 = Stream::factory(''); + Utils::copyToStream($s1, $s2, 10); + $this->assertEquals('', (string) $s2); + } + + public function testReadsLines() + { + $s = Stream::factory("foo" . PHP_EOL . "baz" . PHP_EOL . "bar"); + $this->assertEquals("foo" . PHP_EOL, Utils::readline($s)); + $this->assertEquals("baz" . PHP_EOL, Utils::readline($s)); + $this->assertEquals("bar", Utils::readline($s)); + } + + public function testReadsLinesUpToMaxLength() + { + $s = Stream::factory("12345" . PHP_EOL); + $this->assertEquals("123", Utils::readline($s, 3)); + $this->assertEquals("45" . PHP_EOL, Utils::readline($s)); + } + + public function testReadsLinesWithCustomEol() + { + $s = Stream::factory("foo\tbaz\t\tbar"); + $this->assertEquals("foo\tbaz\t\t", Utils::readline($s, null, "\t\t")); + $this->assertEquals("bar", Utils::readline($s)); + } + + public function testReadsLineUntilFalseReturnedFromRead() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\Stream') + ->setMethods(['read', 'eof']) + ->disableOriginalConstructor() + ->getMock(); + $s->expects($this->exactly(2)) + ->method('read') + ->will($this->returnCallback(function () { + static $c = false; + if ($c) { + return false; + } + $c = true; + return 'h'; + })); + $s->expects($this->exactly(2)) + ->method('eof') + ->will($this->returnValue(false)); + $this->assertEquals("h", Utils::readline($s)); + } + + public function testCalculatesHash() + { + $s = Stream::factory('foobazbar'); + $this->assertEquals(md5('foobazbar'), Utils::hash($s, 'md5')); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\SeekException + */ + public function testCalculatesHashThrowsWhenSeekFails() + { + $s = new NoSeekStream(Stream::factory('foobazbar')); + $s->read(2); + Utils::hash($s, 'md5'); + } + + public function testCalculatesHashSeeksToOriginalPosition() + { + $s = Stream::factory('foobazbar'); + $s->seek(4); + $this->assertEquals(md5('foobazbar'), Utils::hash($s, 'md5')); + $this->assertEquals(4, $s->tell()); + } + + public function testOpensFilesSuccessfully() + { + $r = Utils::open(__FILE__, 'r'); + $this->assertInternalType('resource', $r); + fclose($r); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to open /path/to/does/not/exist using mode r + */ + public function testThrowsExceptionNotWarning() + { + Utils::open('/path/to/does/not/exist', 'r'); + } + + public function testProxiesToFactory() + { + $this->assertEquals('foo', (string) Utils::create('foo')); + } +} diff --git a/lib/Ebanx/vendor/react/promise/CHANGELOG.md b/lib/Ebanx/vendor/react/promise/CHANGELOG.md new file mode 100644 index 00000000..484e542d --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/CHANGELOG.md @@ -0,0 +1,96 @@ +CHANGELOG for 2.x +================= + +* 2.5.1 (2017-03-25) + + * Fix circular references when resolving with a promise which follows + itself (#94). + +* 2.5.0 (2016-12-22) + + * Revert automatic cancellation of pending collection promises once the + output promise resolves. This was introduced in 42d86b7 (PR #36, released + in [v2.3.0](https://github.com/reactphp/promise/releases/tag/v2.3.0)) and + was both unintended and backward incompatible. + + If you need automatic cancellation, you can use something like: + + ```php + function allAndCancel(array $promises) + { + return \React\Promise\all($promises) + ->always(function() use ($promises) { + foreach ($promises as $promise) { + if ($promise instanceof \React\Promise\CancellablePromiseInterface) { + $promise->cancel(); + } + } + }); + } + ``` + * `all()` and `map()` functions now preserve the order of the array (#77). + * Fix circular references when resolving a promise with itself (#71). + +* 2.4.1 (2016-05-03) + + * Fix `some()` not cancelling pending promises when too much input promises + reject (16ff799). + +* 2.4.0 (2016-03-31) + + * Support foreign thenables in `resolve()`. + Any object that provides a `then()` method is now assimilated to a trusted + promise that follows the state of this thenable (#52). + * Fix `some()` and `any()` for input arrays containing not enough items + (#34). + +* 2.3.0 (2016-03-24) + + * Allow cancellation of promises returned by functions working on promise + collections (#36). + * Handle `\Throwable` in the same way as `\Exception` (#51 by @joshdifabio). + +* 2.2.2 (2016-02-26) + + * Fix cancellation handlers called multiple times (#47 by @clue). + +* 2.2.1 (2015-07-03) + + * Fix stack error when resolving a promise in its own fulfillment or + rejection handlers. + +* 2.2.0 (2014-12-30) + + * Introduce new `ExtendedPromiseInterface` implemented by all promises. + * Add new `done()` method (part of the `ExtendedPromiseInterface`). + * Add new `otherwise()` method (part of the `ExtendedPromiseInterface`). + * Add new `always()` method (part of the `ExtendedPromiseInterface`). + * Add new `progress()` method (part of the `ExtendedPromiseInterface`). + * Rename `Deferred::progress` to `Deferred::notify` to avoid confusion with + `ExtendedPromiseInterface::progress` (a `Deferred::progress` alias is + still available for backward compatibility) + * `resolve()` now always returns a `ExtendedPromiseInterface`. + +* 2.1.0 (2014-10-15) + + * Introduce new `CancellablePromiseInterface` implemented by all promises. + * Add new `cancel()` method (part of the `CancellablePromiseInterface`). + +* 2.0.0 (2013-12-10) + + New major release. The goal is to streamline the API and to make it more + compliant with other promise libraries and especially with the new upcoming + [ES6 promises specification](https://github.com/domenic/promises-unwrapping/). + + * Add standalone Promise class. + * Add new `race()` function. + * BC break: Bump minimum PHP version to PHP 5.4. + * BC break: Remove `ResolverInterface` and `PromiseInterface` from + `Deferred`. + * BC break: Change signature of `PromiseInterface`. + * BC break: Remove `When` and `Util` classes and move static methods to + functions. + * BC break: `FulfilledPromise` and `RejectedPromise` now throw an exception + when initialized with a promise instead of a value/reason. + * BC break: `Deferred::resolve()` and `Deferred::reject()` no longer return + a promise. diff --git a/lib/Ebanx/vendor/react/promise/LICENSE b/lib/Ebanx/vendor/react/promise/LICENSE new file mode 100644 index 00000000..5919d20f --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012-2016 Jan Sorgalla + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/Ebanx/vendor/react/promise/README.md b/lib/Ebanx/vendor/react/promise/README.md new file mode 100644 index 00000000..4e6588b0 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/README.md @@ -0,0 +1,840 @@ +Promise +======= + +A lightweight implementation of +[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. + +[![Build Status](https://travis-ci.org/reactphp/promise.svg?branch=master)](http://travis-ci.org/reactphp/promise) +[![Coverage Status](https://coveralls.io/repos/github/reactphp/promise/badge.svg?branch=master)](https://coveralls.io/github/reactphp/promise?branch=master) + +Table of Contents +----------------- + +1. [Introduction](#introduction) +2. [Concepts](#concepts) + * [Deferred](#deferred) + * [Promise](#promise-1) +3. [API](#api) + * [Deferred](#deferred-1) + * [Deferred::promise()](#deferredpromise) + * [Deferred::resolve()](#deferredresolve) + * [Deferred::reject()](#deferredreject) + * [Deferred::notify()](#deferrednotify) + * [PromiseInterface](#promiseinterface) + * [PromiseInterface::then()](#promiseinterfacethen) + * [ExtendedPromiseInterface](#extendedpromiseinterface) + * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) + * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise) + * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways) + * [ExtendedPromiseInterface::progress()](#extendedpromiseinterfaceprogress) + * [CancellablePromiseInterface](#cancellablepromiseinterface) + * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel) + * [Promise](#promise-2) + * [FulfilledPromise](#fulfilledpromise) + * [RejectedPromise](#rejectedpromise) + * [LazyPromise](#lazypromise) + * [Functions](#functions) + * [resolve()](#resolve) + * [reject()](#reject) + * [all()](#all) + * [race()](#race) + * [any()](#any) + * [some()](#some) + * [map()](#map) + * [reduce()](#reduce) + * [PromisorInterface](#promisorinterface) +4. [Examples](#examples) + * [How to use Deferred](#how-to-use-deferred) + * [How promise forwarding works](#how-promise-forwarding-works) + * [Resolution forwarding](#resolution-forwarding) + * [Rejection forwarding](#rejection-forwarding) + * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding) + * [Progress event forwarding](#progress-event-forwarding) + * [done() vs. then()](#done-vs-then) +5. [Credits](#credits) +6. [License](#license) + +Introduction +------------ + +Promise is a library implementing +[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. + +It also provides several other useful promise-related concepts, such as joining +multiple promises and mapping and reducing collections of promises. + +If you've never heard about promises before, +[read this first](https://gist.github.com/3889970). + +Concepts +-------- + +### Deferred + +A **Deferred** represents a computation or unit of work that may not have +completed yet. Typically (but not always), that computation will be something +that executes asynchronously and completes at some point in the future. + +### Promise + +While a deferred represents the computation itself, a **Promise** represents +the result of that computation. Thus, each deferred has a promise that acts as +a placeholder for its actual result. + +API +--- + +### Deferred + +A deferred represents an operation whose resolution is pending. It has separate +promise and resolver parts. + +```php +$deferred = new React\Promise\Deferred(); + +$promise = $deferred->promise(); + +$deferred->resolve(mixed $value = null); +$deferred->reject(mixed $reason = null); +$deferred->notify(mixed $update = null); +``` + +The `promise` method returns the promise of the deferred. + +The `resolve` and `reject` methods control the state of the deferred. + +The `notify` method is for progress notification. + +The constructor of the `Deferred` accepts an optional `$canceller` argument. +See [Promise](#promise-2) for more information. + +#### Deferred::promise() + +```php +$promise = $deferred->promise(); +``` + +Returns the promise of the deferred, which you can hand out to others while +keeping the authority to modify its state to yourself. + +#### Deferred::resolve() + +```php +$deferred->resolve(mixed $value = null); +``` + +Resolves the promise returned by `promise()`. All consumers are notified by +having `$onFulfilled` (which they registered via `$promise->then()`) called with +`$value`. + +If `$value` itself is a promise, the promise will transition to the state of +this promise once it is resolved. + +#### Deferred::reject() + +```php +$deferred->reject(mixed $reason = null); +``` + +Rejects the promise returned by `promise()`, signalling that the deferred's +computation failed. +All consumers are notified by having `$onRejected` (which they registered via +`$promise->then()`) called with `$reason`. + +If `$reason` itself is a promise, the promise will be rejected with the outcome +of this promise regardless whether it fulfills or rejects. + +#### Deferred::notify() + +```php +$deferred->notify(mixed $update = null); +``` + +Triggers progress notifications, to indicate to consumers that the computation +is making progress toward its result. + +All consumers are notified by having `$onProgress` (which they registered via +`$promise->then()`) called with `$update`. + +### PromiseInterface + +The promise interface provides the common interface for all promise +implementations. + +A promise represents an eventual outcome, which is either fulfillment (success) +and an associated value, or rejection (failure) and an associated reason. + +Once in the fulfilled or rejected state, a promise becomes immutable. +Neither its state nor its result (or error) can be modified. + +#### Implementations + +* [Promise](#promise-2) +* [FulfilledPromise](#fulfilledpromise) +* [RejectedPromise](#rejectedpromise) +* [LazyPromise](#lazypromise) + +#### PromiseInterface::then() + +```php +$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); +``` + +Transforms a promise's value by applying a function to the promise's fulfillment +or rejection value. Returns a new promise for the transformed result. + +The `then()` method registers new fulfilled, rejection and progress handlers +with a promise (all parameters are optional): + + * `$onFulfilled` will be invoked once the promise is fulfilled and passed + the result as the first argument. + * `$onRejected` will be invoked once the promise is rejected and passed the + reason as the first argument. + * `$onProgress` will be invoked whenever the producer of the promise + triggers progress notifications and passed a single argument (whatever it + wants) to indicate progress. + +It returns a new promise that will fulfill with the return value of either +`$onFulfilled` or `$onRejected`, whichever is called, or will reject with +the thrown exception if either throws. + +A promise makes the following guarantees about handlers registered in +the same call to `then()`: + + 1. Only one of `$onFulfilled` or `$onRejected` will be called, + never both. + 2. `$onFulfilled` and `$onRejected` will never be called more + than once. + 3. `$onProgress` may be called multiple times. + +#### See also + +* [resolve()](#resolve) - Creating a resolved promise +* [reject()](#reject) - Creating a rejected promise +* [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) +* [done() vs. then()](#done-vs-then) + +### ExtendedPromiseInterface + +The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut +and utility methods which are not part of the Promises/A specification. + +#### Implementations + +* [Promise](#promise-1) +* [FulfilledPromise](#fulfilledpromise) +* [RejectedPromise](#rejectedpromise) +* [LazyPromise](#lazypromise) + +#### ExtendedPromiseInterface::done() + +```php +$promise->done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); +``` + +Consumes the promise's ultimate value if the promise fulfills, or handles the +ultimate error. + +It will cause a fatal error if either `$onFulfilled` or `$onRejected` throw or +return a rejected promise. + +Since the purpose of `done()` is consumption rather than transformation, +`done()` always returns `null`. + +#### See also + +* [PromiseInterface::then()](#promiseinterfacethen) +* [done() vs. then()](#done-vs-then) + +#### ExtendedPromiseInterface::otherwise() + +```php +$promise->otherwise(callable $onRejected); +``` + +Registers a rejection handler for promise. It is a shortcut for: + +```php +$promise->then(null, $onRejected); +``` + +Additionally, you can type hint the `$reason` argument of `$onRejected` to catch +only specific errors. + +```php +$promise + ->otherwise(function (\RuntimeException $reason) { + // Only catch \RuntimeException instances + // All other types of errors will propagate automatically + }) + ->otherwise(function ($reason) { + // Catch other errors + )}; +``` + +#### ExtendedPromiseInterface::always() + +```php +$newPromise = $promise->always(callable $onFulfilledOrRejected); +``` + +Allows you to execute "cleanup" type tasks in a promise chain. + +It arranges for `$onFulfilledOrRejected` to be called, with no arguments, +when the promise is either fulfilled or rejected. + +* If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully, + `$newPromise` will fulfill with the same value as `$promise`. +* If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a + rejected promise, `$newPromise` will reject with the thrown exception or + rejected promise's reason. +* If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully, + `$newPromise` will reject with the same reason as `$promise`. +* If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a + rejected promise, `$newPromise` will reject with the thrown exception or + rejected promise's reason. + +`always()` behaves similarly to the synchronous finally statement. When combined +with `otherwise()`, `always()` allows you to write code that is similar to the familiar +synchronous catch/finally pair. + +Consider the following synchronous code: + +```php +try { + return doSomething(); +} catch(\Exception $e) { + return handleError($e); +} finally { + cleanup(); +} +``` + +Similar asynchronous code (with `doSomething()` that returns a promise) can be +written: + +```php +return doSomething() + ->otherwise('handleError') + ->always('cleanup'); +``` + +#### ExtendedPromiseInterface::progress() + +```php +$promise->progress(callable $onProgress); +``` + +Registers a handler for progress updates from promise. It is a shortcut for: + +```php +$promise->then(null, null, $onProgress); +``` + +### CancellablePromiseInterface + +A cancellable promise provides a mechanism for consumers to notify the creator +of the promise that they are not longer interested in the result of an +operation. + +#### CancellablePromiseInterface::cancel() + +``` php +$promise->cancel(); +``` + +The `cancel()` method notifies the creator of the promise that there is no +further interest in the results of the operation. + +Once a promise is settled (either fulfilled or rejected), calling `cancel()` on +a promise has no effect. + +#### Implementations + +* [Promise](#promise-1) +* [FulfilledPromise](#fulfilledpromise) +* [RejectedPromise](#rejectedpromise) +* [LazyPromise](#lazypromise) + +### Promise + +Creates a promise whose state is controlled by the functions passed to +`$resolver`. + +```php +$resolver = function (callable $resolve, callable $reject, callable $notify) { + // Do some work, possibly asynchronously, and then + // resolve or reject. You can notify of progress events + // along the way if you want/need. + + $resolve($awesomeResult); + // or $resolve($anotherPromise); + // or $reject($nastyError); + // or $notify($progressNotification); +}; + +$canceller = function (callable $resolve, callable $reject, callable $progress) { + // Cancel/abort any running operations like network connections, streams etc. + + $reject(new \Exception('Promise cancelled')); +}; + +$promise = new React\Promise\Promise($resolver, $canceller); +``` + +The promise constructor receives a resolver function and an optional canceller +function which both will be called with 3 arguments: + + * `$resolve($value)` - Primary function that seals the fate of the + returned promise. Accepts either a non-promise value, or another promise. + When called with a non-promise value, fulfills promise with that value. + When called with another promise, e.g. `$resolve($otherPromise)`, promise's + fate will be equivalent to that of `$otherPromise`. + * `$reject($reason)` - Function that rejects the promise. + * `$notify($update)` - Function that issues progress events for the promise. + +If the resolver or canceller throw an exception, the promise will be rejected +with that thrown exception as the rejection reason. + +The resolver function will be called immediately, the canceller function only +once all consumers called the `cancel()` method of the promise. + +### FulfilledPromise + +Creates a already fulfilled promise. + +```php +$promise = React\Promise\FulfilledPromise($value); +``` + +Note, that `$value` **cannot** be a promise. It's recommended to use +[resolve()](#resolve) for creating resolved promises. + +### RejectedPromise + +Creates a already rejected promise. + +```php +$promise = React\Promise\RejectedPromise($reason); +``` + +Note, that `$reason` **cannot** be a promise. It's recommended to use +[reject()](#reject) for creating rejected promises. + +### LazyPromise + +Creates a promise which will be lazily initialized by `$factory` once a consumer +calls the `then()` method. + +```php +$factory = function () { + $deferred = new React\Promise\Deferred(); + + // Do some heavy stuff here and resolve the deferred once completed + + return $deferred->promise(); +}; + +$promise = React\Promise\LazyPromise($factory); + +// $factory will only be executed once we call then() +$promise->then(function ($value) { +}); +``` + +### Functions + +Useful functions for creating, joining, mapping and reducing collections of +promises. + +All functions working on promise collections (like `all()`, `race()`, `some()` +etc.) support cancellation. This means, if you call `cancel()` on the returned +promise, all promises in the collection are cancelled. If the collection itself +is a promise which resolves to an array, this promise is also cancelled. + +#### resolve() + +```php +$promise = React\Promise\resolve(mixed $promiseOrValue); +``` + +Creates a promise for the supplied `$promiseOrValue`. + +If `$promiseOrValue` is a value, it will be the resolution value of the +returned promise. + +If `$promiseOrValue` is a thenable (any object that provides a `then()` method), +a trusted promise that follows the state of the thenable is returned. + +If `$promiseOrValue` is a promise, it will be returned as is. + +Note: The promise returned is always a promise implementing +[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom +promise which only implements [PromiseInterface](#promiseinterface), this +promise will be assimilated to a extended promise following `$promiseOrValue`. + +#### reject() + +```php +$promise = React\Promise\reject(mixed $promiseOrValue); +``` + +Creates a rejected promise for the supplied `$promiseOrValue`. + +If `$promiseOrValue` is a value, it will be the rejection value of the +returned promise. + +If `$promiseOrValue` is a promise, its completion value will be the rejected +value of the returned promise. + +This can be useful in situations where you need to reject a promise without +throwing an exception. For example, it allows you to propagate a rejection with +the value of another promise. + +#### all() + +```php +$promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues); +``` + +Returns a promise that will resolve only once all the items in +`$promisesOrValues` have resolved. The resolution value of the returned promise +will be an array containing the resolution values of each of the items in +`$promisesOrValues`. + +#### race() + +```php +$promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues); +``` + +Initiates a competitive race that allows one winner. Returns a promise which is +resolved in the same way the first settled promise resolves. + +#### any() + +```php +$promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues); +``` + +Returns a promise that will resolve when any one of the items in +`$promisesOrValues` resolves. The resolution value of the returned promise +will be the resolution value of the triggering item. + +The returned promise will only reject if *all* items in `$promisesOrValues` are +rejected. The rejection value will be an array of all rejection reasons. + +The returned promise will also reject with a `React\Promise\Exception\LengthException` +if `$promisesOrValues` contains 0 items. + +#### some() + +```php +$promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany); +``` + +Returns a promise that will resolve when `$howMany` of the supplied items in +`$promisesOrValues` resolve. The resolution value of the returned promise +will be an array of length `$howMany` containing the resolution values of the +triggering items. + +The returned promise will reject if it becomes impossible for `$howMany` items +to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items +reject). The rejection value will be an array of +`(count($promisesOrValues) - $howMany) + 1` rejection reasons. + +The returned promise will also reject with a `React\Promise\Exception\LengthException` +if `$promisesOrValues` contains less items than `$howMany`. + +#### map() + +```php +$promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc); +``` + +Traditional map function, similar to `array_map()`, but allows input to contain +promises and/or values, and `$mapFunc` may return either a value or a promise. + +The map function receives each item as argument, where item is a fully resolved +value of a promise or value in `$promisesOrValues`. + +#### reduce() + +```php +$promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null); +``` + +Traditional reduce function, similar to `array_reduce()`, but input may contain +promises and/or values, and `$reduceFunc` may return either a value or a +promise, *and* `$initialValue` may be a promise or a value for the starting +value. + +### PromisorInterface + +The `React\Promise\PromisorInterface` provides a common interface for objects +that provide a promise. `React\Promise\Deferred` implements it, but since it +is part of the public API anyone can implement it. + +Examples +-------- + +### How to use Deferred + +```php +function getAwesomeResultPromise() +{ + $deferred = new React\Promise\Deferred(); + + // Execute a Node.js-style function using the callback pattern + computeAwesomeResultAsynchronously(function ($error, $result) use ($deferred) { + if ($error) { + $deferred->reject($error); + } else { + $deferred->resolve($result); + } + }); + + // Return the promise + return $deferred->promise(); +} + +getAwesomeResultPromise() + ->then( + function ($value) { + // Deferred resolved, do something with $value + }, + function ($reason) { + // Deferred rejected, do something with $reason + }, + function ($update) { + // Progress notification triggered, do something with $update + } + ); +``` + +### How promise forwarding works + +A few simple examples to show how the mechanics of Promises/A forwarding works. +These examples are contrived, of course, and in real usage, promise chains will +typically be spread across several function calls, or even several levels of +your application architecture. + +#### Resolution forwarding + +Resolved promises forward resolution values to the next promise. +The first promise, `$deferred->promise()`, will resolve with the value passed +to `$deferred->resolve()` below. + +Each call to `then()` returns a new promise that will resolve with the return +value of the previous handler. This creates a promise "pipeline". + +```php +$deferred = new React\Promise\Deferred(); + +$deferred->promise() + ->then(function ($x) { + // $x will be the value passed to $deferred->resolve() below + // and returns a *new promise* for $x + 1 + return $x + 1; + }) + ->then(function ($x) { + // $x === 2 + // This handler receives the return value of the + // previous handler. + return $x + 1; + }) + ->then(function ($x) { + // $x === 3 + // This handler receives the return value of the + // previous handler. + return $x + 1; + }) + ->then(function ($x) { + // $x === 4 + // This handler receives the return value of the + // previous handler. + echo 'Resolve ' . $x; + }); + +$deferred->resolve(1); // Prints "Resolve 4" +``` + +#### Rejection forwarding + +Rejected promises behave similarly, and also work similarly to try/catch: +When you catch an exception, you must rethrow for it to propagate. + +Similarly, when you handle a rejected promise, to propagate the rejection, +"rethrow" it by either returning a rejected promise, or actually throwing +(since promise translates thrown exceptions into rejections) + +```php +$deferred = new React\Promise\Deferred(); + +$deferred->promise() + ->then(function ($x) { + throw new \Exception($x + 1); + }) + ->otherwise(function (\Exception $x) { + // Propagate the rejection + throw $x; + }) + ->otherwise(function (\Exception $x) { + // Can also propagate by returning another rejection + return React\Promise\reject( + new \Exception($x->getMessage() + 1) + ); + }) + ->otherwise(function ($x) { + echo 'Reject ' . $x->getMessage(); // 3 + }); + +$deferred->resolve(1); // Prints "Reject 3" +``` + +#### Mixed resolution and rejection forwarding + +Just like try/catch, you can choose to propagate or not. Mixing resolutions and +rejections will still forward handler results in a predictable way. + +```php +$deferred = new React\Promise\Deferred(); + +$deferred->promise() + ->then(function ($x) { + return $x + 1; + }) + ->then(function ($x) { + throw new \Exception($x + 1); + }) + ->otherwise(function (\Exception $x) { + // Handle the rejection, and don't propagate. + // This is like catch without a rethrow + return $x->getMessage() + 1; + }) + ->then(function ($x) { + echo 'Mixed ' . $x; // 4 + }); + +$deferred->resolve(1); // Prints "Mixed 4" +``` + +#### Progress event forwarding + +In the same way as resolution and rejection handlers, your progress handler +**MUST** return a progress event to be propagated to the next link in the chain. +If you return nothing, `null` will be propagated. + +Also in the same way as resolutions and rejections, if you don't register a +progress handler, the update will be propagated through. + +If your progress handler throws an exception, the exception will be propagated +to the next link in the chain. The best thing to do is to ensure your progress +handlers do not throw exceptions. + +This gives you the opportunity to transform progress events at each step in the +chain so that they are meaningful to the next step. It also allows you to choose +not to transform them, and simply let them propagate untransformed, by not +registering a progress handler. + +```php +$deferred = new React\Promise\Deferred(); + +$deferred->promise() + ->progress(function ($update) { + return $update + 1; + }) + ->progress(function ($update) { + echo 'Progress ' . $update; // 2 + }); + +$deferred->notify(1); // Prints "Progress 2" +``` + +### done() vs. then() + +The golden rule is: + + Either return your promise, or call done() on it. + +At a first glance, `then()` and `done()` seem very similar. However, there are +important distinctions. + +The intent of `then()` is to transform a promise's value and to pass or return +a new promise for the transformed value along to other parts of your code. + +The intent of `done()` is to consume a promise's value, transferring +responsibility for the value to your code. + +In addition to transforming a value, `then()` allows you to recover from, or +propagate intermediate errors. Any errors that are not handled will be caught +by the promise machinery and used to reject the promise returned by `then()`. + +Calling `done()` transfers all responsibility for errors to your code. If an +error (either a thrown exception or returned rejection) escapes the +`$onFulfilled` or `$onRejected` callbacks you provide to done, it will be +rethrown in an uncatchable way causing a fatal error. + +```php +function getJsonResult() +{ + return queryApi() + ->then( + // Transform API results to an object + function ($jsonResultString) { + return json_decode($jsonResultString); + }, + // Transform API errors to an exception + function ($jsonErrorString) { + $object = json_decode($jsonErrorString); + throw new ApiErrorException($object->errorMessage); + } + ); +} + +// Here we provide no rejection handler. If the promise returned has been +// rejected, the ApiErrorException will be thrown +getJsonResult() + ->done( + // Consume transformed object + function ($jsonResultObject) { + // Do something with $jsonResultObject + } + ); + +// Here we provide a rejection handler which will either throw while debugging +// or log the exception +getJsonResult() + ->done( + function ($jsonResultObject) { + // Do something with $jsonResultObject + }, + function (ApiErrorException $exception) { + if (isDebug()) { + throw $exception; + } else { + logException($exception); + } + } + ); +``` + +Note that if a rejection value is not an instance of `\Exception`, it will be +wrapped in an exception of the type `React\Promise\UnhandledRejectionException`. + +You can get the original rejection reason by calling `$exception->getReason()`. + +Credits +------- + +Promise is a port of [when.js](https://github.com/cujojs/when) +by [Brian Cavalier](https://github.com/briancavalier). + +Also, large parts of the documentation have been ported from the when.js +[Wiki](https://github.com/cujojs/when/wiki) and the +[API docs](https://github.com/cujojs/when/blob/master/docs/api.md). + +License +------- + +Released under the [MIT](LICENSE) license. diff --git a/lib/Ebanx/vendor/react/promise/composer.json b/lib/Ebanx/vendor/react/promise/composer.json new file mode 100644 index 00000000..2fc48091 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/composer.json @@ -0,0 +1,29 @@ +{ + "name": "react/promise", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "license": "MIT", + "authors": [ + {"name": "Jan Sorgalla", "email": "jsorgalla@gmail.com"} + ], + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": ["src/functions_include.php"] + }, + "autoload-dev": { + "psr-4": { + "React\\Promise\\": "tests/fixtures" + } + }, + "keywords": [ + "promise", + "promises" + ] +} diff --git a/lib/Ebanx/vendor/react/promise/phpunit.xml.dist b/lib/Ebanx/vendor/react/promise/phpunit.xml.dist new file mode 100644 index 00000000..b9a689d7 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + ./tests/ + + + + + + ./src/ + + ./src/functions_include.php + + + + diff --git a/lib/Ebanx/vendor/react/promise/src/CancellablePromiseInterface.php b/lib/Ebanx/vendor/react/promise/src/CancellablePromiseInterface.php new file mode 100644 index 00000000..896db2d3 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/CancellablePromiseInterface.php @@ -0,0 +1,11 @@ +started) { + return; + } + + $this->started = true; + $this->drain(); + } + + public function enqueue($cancellable) + { + if (!method_exists($cancellable, 'then') || !method_exists($cancellable, 'cancel')) { + return; + } + + $length = array_push($this->queue, $cancellable); + + if ($this->started && 1 === $length) { + $this->drain(); + } + } + + private function drain() + { + for ($i = key($this->queue); isset($this->queue[$i]); $i++) { + $cancellable = $this->queue[$i]; + + $exception = null; + + try { + $cancellable->cancel(); + } catch (\Throwable $exception) { + } catch (\Exception $exception) { + } + + unset($this->queue[$i]); + + if ($exception) { + throw $exception; + } + } + + $this->queue = []; + } +} diff --git a/lib/Ebanx/vendor/react/promise/src/Deferred.php b/lib/Ebanx/vendor/react/promise/src/Deferred.php new file mode 100644 index 00000000..f23980c3 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/Deferred.php @@ -0,0 +1,60 @@ +canceller = $canceller; + } + + public function promise() + { + if (null === $this->promise) { + $this->promise = new Promise(function ($resolve, $reject, $notify) { + $this->resolveCallback = $resolve; + $this->rejectCallback = $reject; + $this->notifyCallback = $notify; + }, $this->canceller); + } + + return $this->promise; + } + + public function resolve($value = null) + { + $this->promise(); + + call_user_func($this->resolveCallback, $value); + } + + public function reject($reason = null) + { + $this->promise(); + + call_user_func($this->rejectCallback, $reason); + } + + public function notify($update = null) + { + $this->promise(); + + call_user_func($this->notifyCallback, $update); + } + + /** + * @deprecated 2.2.0 + * @see Deferred::notify() + */ + public function progress($update = null) + { + $this->notify($update); + } +} diff --git a/lib/Ebanx/vendor/react/promise/src/Exception/LengthException.php b/lib/Ebanx/vendor/react/promise/src/Exception/LengthException.php new file mode 100644 index 00000000..775c48db --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/Exception/LengthException.php @@ -0,0 +1,7 @@ +value = $value; + } + + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null === $onFulfilled) { + return $this; + } + + try { + return resolve($onFulfilled($this->value)); + } catch (\Throwable $exception) { + return new RejectedPromise($exception); + } catch (\Exception $exception) { + return new RejectedPromise($exception); + } + } + + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null === $onFulfilled) { + return; + } + + $result = $onFulfilled($this->value); + + if ($result instanceof ExtendedPromiseInterface) { + $result->done(); + } + } + + public function otherwise(callable $onRejected) + { + return $this; + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->then(function ($value) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($value) { + return $value; + }); + }); + } + + public function progress(callable $onProgress) + { + return $this; + } + + public function cancel() + { + } +} diff --git a/lib/Ebanx/vendor/react/promise/src/LazyPromise.php b/lib/Ebanx/vendor/react/promise/src/LazyPromise.php new file mode 100644 index 00000000..7e3a3d3d --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/LazyPromise.php @@ -0,0 +1,63 @@ +factory = $factory; + } + + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + return $this->promise()->then($onFulfilled, $onRejected, $onProgress); + } + + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + return $this->promise()->done($onFulfilled, $onRejected, $onProgress); + } + + public function otherwise(callable $onRejected) + { + return $this->promise()->otherwise($onRejected); + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->promise()->always($onFulfilledOrRejected); + } + + public function progress(callable $onProgress) + { + return $this->promise()->progress($onProgress); + } + + public function cancel() + { + return $this->promise()->cancel(); + } + + /** + * @internal + * @see Promise::settle() + */ + public function promise() + { + if (null === $this->promise) { + try { + $this->promise = resolve(call_user_func($this->factory)); + } catch (\Throwable $exception) { + $this->promise = new RejectedPromise($exception); + } catch (\Exception $exception) { + $this->promise = new RejectedPromise($exception); + } + } + + return $this->promise; + } +} diff --git a/lib/Ebanx/vendor/react/promise/src/Promise.php b/lib/Ebanx/vendor/react/promise/src/Promise.php new file mode 100644 index 00000000..889926df --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/Promise.php @@ -0,0 +1,242 @@ +canceller = $canceller; + $this->call($resolver); + } + + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null !== $this->result) { + return $this->result->then($onFulfilled, $onRejected, $onProgress); + } + + if (null === $this->canceller) { + return new static($this->resolver($onFulfilled, $onRejected, $onProgress)); + } + + $this->requiredCancelRequests++; + + return new static($this->resolver($onFulfilled, $onRejected, $onProgress), function () { + $this->requiredCancelRequests--; + + if ($this->requiredCancelRequests <= 0) { + $this->cancel(); + } + }); + } + + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null !== $this->result) { + return $this->result->done($onFulfilled, $onRejected, $onProgress); + } + + $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected) { + $promise + ->done($onFulfilled, $onRejected); + }; + + if ($onProgress) { + $this->progressHandlers[] = $onProgress; + } + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, function ($reason) use ($onRejected) { + if (!_checkTypehint($onRejected, $reason)) { + return new RejectedPromise($reason); + } + + return $onRejected($reason); + }); + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->then(function ($value) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($value) { + return $value; + }); + }, function ($reason) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($reason) { + return new RejectedPromise($reason); + }); + }); + } + + public function progress(callable $onProgress) + { + return $this->then(null, null, $onProgress); + } + + public function cancel() + { + $canceller = $this->canceller; + $this->canceller = null; + + $parentCanceller = null; + + if (null !== $this->result) { + // Go up the promise chain and reach the top most promise which is + // itself not following another promise + $root = $this->unwrap($this->result); + + // Return if the root promise is already resolved or a + // FulfilledPromise or RejectedPromise + if (!$root instanceof self || null !== $root->result) { + return; + } + + $root->requiredCancelRequests--; + + if ($root->requiredCancelRequests <= 0) { + $parentCanceller = [$root, 'cancel']; + } + } + + if (null !== $canceller) { + $this->call($canceller); + } + + // For BC, we call the parent canceller after our own canceller + if ($parentCanceller) { + $parentCanceller(); + } + } + + private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) { + if ($onProgress) { + $progressHandler = function ($update) use ($notify, $onProgress) { + try { + $notify($onProgress($update)); + } catch (\Throwable $e) { + $notify($e); + } catch (\Exception $e) { + $notify($e); + } + }; + } else { + $progressHandler = $notify; + } + + $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) { + $promise + ->then($onFulfilled, $onRejected) + ->done($resolve, $reject, $progressHandler); + }; + + $this->progressHandlers[] = $progressHandler; + }; + } + + private function resolve($value = null) + { + if (null !== $this->result) { + return; + } + + $this->settle(resolve($value)); + } + + private function reject($reason = null) + { + if (null !== $this->result) { + return; + } + + $this->settle(reject($reason)); + } + + private function notify($update = null) + { + if (null !== $this->result) { + return; + } + + foreach ($this->progressHandlers as $handler) { + $handler($update); + } + } + + private function settle(ExtendedPromiseInterface $promise) + { + $promise = $this->unwrap($promise); + + if ($promise === $this) { + $promise = new RejectedPromise( + new \LogicException('Cannot resolve a promise with itself.') + ); + } + + if ($promise instanceof self) { + $promise->requiredCancelRequests++; + } + + $handlers = $this->handlers; + + $this->progressHandlers = $this->handlers = []; + $this->result = $promise; + + foreach ($handlers as $handler) { + $handler($promise); + } + } + + private function unwrap($promise) + { + $promise = $this->extract($promise); + + while ($promise instanceof self && null !== $promise->result) { + $promise = $this->extract($promise->result); + } + + return $promise; + } + + private function extract($promise) + { + if ($promise instanceof LazyPromise) { + $promise = $promise->promise(); + } + + return $promise; + } + + private function call(callable $callback) + { + try { + $callback( + function ($value = null) { + $this->resolve($value); + }, + function ($reason = null) { + $this->reject($reason); + }, + function ($update = null) { + $this->notify($update); + } + ); + } catch (\Throwable $e) { + $this->reject($e); + } catch (\Exception $e) { + $this->reject($e); + } + } +} diff --git a/lib/Ebanx/vendor/react/promise/src/PromiseInterface.php b/lib/Ebanx/vendor/react/promise/src/PromiseInterface.php new file mode 100644 index 00000000..d80d1142 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/PromiseInterface.php @@ -0,0 +1,11 @@ +reason = $reason; + } + + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null === $onRejected) { + return $this; + } + + try { + return resolve($onRejected($this->reason)); + } catch (\Throwable $exception) { + return new RejectedPromise($exception); + } catch (\Exception $exception) { + return new RejectedPromise($exception); + } + } + + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null === $onRejected) { + throw UnhandledRejectionException::resolve($this->reason); + } + + $result = $onRejected($this->reason); + + if ($result instanceof self) { + throw UnhandledRejectionException::resolve($result->reason); + } + + if ($result instanceof ExtendedPromiseInterface) { + $result->done(); + } + } + + public function otherwise(callable $onRejected) + { + if (!_checkTypehint($onRejected, $this->reason)) { + return $this; + } + + return $this->then(null, $onRejected); + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->then(null, function ($reason) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($reason) { + return new RejectedPromise($reason); + }); + }); + } + + public function progress(callable $onProgress) + { + return $this; + } + + public function cancel() + { + } +} diff --git a/lib/Ebanx/vendor/react/promise/src/UnhandledRejectionException.php b/lib/Ebanx/vendor/react/promise/src/UnhandledRejectionException.php new file mode 100644 index 00000000..a44b7a1b --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/UnhandledRejectionException.php @@ -0,0 +1,31 @@ +reason = $reason; + + $message = sprintf('Unhandled Rejection: %s', json_encode($reason)); + + parent::__construct($message, 0); + } + + public function getReason() + { + return $this->reason; + } +} diff --git a/lib/Ebanx/vendor/react/promise/src/functions.php b/lib/Ebanx/vendor/react/promise/src/functions.php new file mode 100644 index 00000000..70c0eb72 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/functions.php @@ -0,0 +1,244 @@ +then($resolve, $reject, $notify); + }, $canceller); + } + + return new FulfilledPromise($promiseOrValue); +} + +function reject($promiseOrValue = null) +{ + if ($promiseOrValue instanceof PromiseInterface) { + return resolve($promiseOrValue)->then(function ($value) { + return new RejectedPromise($value); + }); + } + + return new RejectedPromise($promiseOrValue); +} + +function all($promisesOrValues) +{ + return map($promisesOrValues, function ($val) { + return $val; + }); +} + +function race($promisesOrValues) +{ + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($promisesOrValues); + + return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $cancellationQueue) { + resolve($promisesOrValues) + ->done(function ($array) use ($cancellationQueue, $resolve, $reject, $notify) { + if (!is_array($array) || !$array) { + $resolve(); + return; + } + + foreach ($array as $promiseOrValue) { + $cancellationQueue->enqueue($promiseOrValue); + + resolve($promiseOrValue) + ->done($resolve, $reject, $notify); + } + }, $reject, $notify); + }, $cancellationQueue); +} + +function any($promisesOrValues) +{ + return some($promisesOrValues, 1) + ->then(function ($val) { + return array_shift($val); + }); +} + +function some($promisesOrValues, $howMany) +{ + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($promisesOrValues); + + return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $howMany, $cancellationQueue) { + resolve($promisesOrValues) + ->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject, $notify) { + if (!is_array($array) || $howMany < 1) { + $resolve([]); + return; + } + + $len = count($array); + + if ($len < $howMany) { + throw new Exception\LengthException( + sprintf( + 'Input array must contain at least %d item%s but contains only %s item%s.', + $howMany, + 1 === $howMany ? '' : 's', + $len, + 1 === $len ? '' : 's' + ) + ); + } + + $toResolve = $howMany; + $toReject = ($len - $toResolve) + 1; + $values = []; + $reasons = []; + + foreach ($array as $i => $promiseOrValue) { + $fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve) { + if ($toResolve < 1 || $toReject < 1) { + return; + } + + $values[$i] = $val; + + if (0 === --$toResolve) { + $resolve($values); + } + }; + + $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) { + if ($toResolve < 1 || $toReject < 1) { + return; + } + + $reasons[$i] = $reason; + + if (0 === --$toReject) { + $reject($reasons); + } + }; + + $cancellationQueue->enqueue($promiseOrValue); + + resolve($promiseOrValue) + ->done($fulfiller, $rejecter, $notify); + } + }, $reject, $notify); + }, $cancellationQueue); +} + +function map($promisesOrValues, callable $mapFunc) +{ + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($promisesOrValues); + + return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $mapFunc, $cancellationQueue) { + resolve($promisesOrValues) + ->done(function ($array) use ($mapFunc, $cancellationQueue, $resolve, $reject, $notify) { + if (!is_array($array) || !$array) { + $resolve([]); + return; + } + + $toResolve = count($array); + $values = []; + + foreach ($array as $i => $promiseOrValue) { + $cancellationQueue->enqueue($promiseOrValue); + $values[$i] = null; + + resolve($promiseOrValue) + ->then($mapFunc) + ->done( + function ($mapped) use ($i, &$values, &$toResolve, $resolve) { + $values[$i] = $mapped; + + if (0 === --$toResolve) { + $resolve($values); + } + }, + $reject, + $notify + ); + } + }, $reject, $notify); + }, $cancellationQueue); +} + +function reduce($promisesOrValues, callable $reduceFunc, $initialValue = null) +{ + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($promisesOrValues); + + return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $reduceFunc, $initialValue, $cancellationQueue) { + resolve($promisesOrValues) + ->done(function ($array) use ($reduceFunc, $initialValue, $cancellationQueue, $resolve, $reject, $notify) { + if (!is_array($array)) { + $array = []; + } + + $total = count($array); + $i = 0; + + // Wrap the supplied $reduceFunc with one that handles promises and then + // delegates to the supplied. + $wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) { + $cancellationQueue->enqueue($val); + + return $current + ->then(function ($c) use ($reduceFunc, $total, &$i, $val) { + return resolve($val) + ->then(function ($value) use ($reduceFunc, $total, &$i, $c) { + return $reduceFunc($c, $value, $i++, $total); + }); + }); + }; + + $cancellationQueue->enqueue($initialValue); + + array_reduce($array, $wrappedReduceFunc, resolve($initialValue)) + ->done($resolve, $reject, $notify); + }, $reject, $notify); + }, $cancellationQueue); +} + +// Internal functions +function _checkTypehint(callable $callback, $object) +{ + if (!is_object($object)) { + return true; + } + + if (is_array($callback)) { + $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]); + } elseif (is_object($callback) && !$callback instanceof \Closure) { + $callbackReflection = new \ReflectionMethod($callback, '__invoke'); + } else { + $callbackReflection = new \ReflectionFunction($callback); + } + + $parameters = $callbackReflection->getParameters(); + + if (!isset($parameters[0])) { + return true; + } + + $expectedException = $parameters[0]; + + if (!$expectedException->getClass()) { + return true; + } + + return $expectedException->getClass()->isInstance($object); +} diff --git a/lib/Ebanx/vendor/react/promise/src/functions_include.php b/lib/Ebanx/vendor/react/promise/src/functions_include.php new file mode 100644 index 00000000..c71decbf --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/src/functions_include.php @@ -0,0 +1,5 @@ +enqueue($p); + + $cancellationQueue(); + + $this->assertTrue($p->cancelCalled); + } + + /** @test */ + public function ignoresSimpleCancellable() + { + $p = new SimpleTestCancellable(); + + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($p); + + $cancellationQueue(); + + $this->assertFalse($p->cancelCalled); + } + + /** @test */ + public function callsCancelOnPromisesEnqueuedBeforeStart() + { + $d1 = $this->getCancellableDeferred(); + $d2 = $this->getCancellableDeferred(); + + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($d1->promise()); + $cancellationQueue->enqueue($d2->promise()); + + $cancellationQueue(); + } + + /** @test */ + public function callsCancelOnPromisesEnqueuedAfterStart() + { + $d1 = $this->getCancellableDeferred(); + $d2 = $this->getCancellableDeferred(); + + $cancellationQueue = new CancellationQueue(); + + $cancellationQueue(); + + $cancellationQueue->enqueue($d2->promise()); + $cancellationQueue->enqueue($d1->promise()); + } + + /** @test */ + public function doesNotCallCancelTwiceWhenStartedTwice() + { + $d = $this->getCancellableDeferred(); + + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($d->promise()); + + $cancellationQueue(); + $cancellationQueue(); + } + + /** @test */ + public function rethrowsExceptionsThrownFromCancel() + { + $this->setExpectedException('\Exception', 'test'); + + $mock = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock + ->expects($this->once()) + ->method('cancel') + ->will($this->throwException(new \Exception('test'))); + + $cancellationQueue = new CancellationQueue(); + $cancellationQueue->enqueue($mock); + + $cancellationQueue(); + } + + private function getCancellableDeferred() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke'); + + return new Deferred($mock); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/DeferredTest.php b/lib/Ebanx/vendor/react/promise/tests/DeferredTest.php new file mode 100644 index 00000000..16212e9e --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/DeferredTest.php @@ -0,0 +1,42 @@ + [$d, 'promise'], + 'resolve' => [$d, 'resolve'], + 'reject' => [$d, 'reject'], + 'notify' => [$d, 'progress'], + 'settle' => [$d, 'resolve'], + ]); + } + + /** @test */ + public function progressIsAnAliasForNotify() + { + $deferred = new Deferred(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + $deferred->promise() + ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); + + $deferred->progress($sentinel); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FulfilledPromiseTest.php b/lib/Ebanx/vendor/react/promise/tests/FulfilledPromiseTest.php new file mode 100644 index 00000000..97fc8f6c --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FulfilledPromiseTest.php @@ -0,0 +1,50 @@ + function () use (&$promise) { + if (!$promise) { + throw new \LogicException('FulfilledPromise must be resolved before obtaining the promise'); + } + + return $promise; + }, + 'resolve' => function ($value = null) use (&$promise) { + if (!$promise) { + $promise = new FulfilledPromise($value); + } + }, + 'reject' => function () { + throw new \LogicException('You cannot call reject() for React\Promise\FulfilledPromise'); + }, + 'notify' => function () { + // no-op + }, + 'settle' => function ($value = null) use (&$promise) { + if (!$promise) { + $promise = new FulfilledPromise($value); + } + }, + ]); + } + + /** @test */ + public function shouldThrowExceptionIfConstructedWithAPromise() + { + $this->setExpectedException('\InvalidArgumentException'); + + return new FulfilledPromise(new FulfilledPromise()); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionAllTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionAllTest.php new file mode 100644 index 00000000..74c1d7c3 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionAllTest.php @@ -0,0 +1,114 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([])); + + all([]) + ->then($mock); + } + + /** @test */ + public function shouldResolveValuesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2, 3])); + + all([1, 2, 3]) + ->then($mock); + } + + /** @test */ + public function shouldResolvePromisesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2, 3])); + + all([resolve(1), resolve(2), resolve(3)]) + ->then($mock); + } + + /** @test */ + public function shouldResolveSparseArrayInput() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([null, 1, null, 1, 1])); + + all([null, 1, null, 1, 1]) + ->then($mock); + } + + /** @test */ + public function shouldRejectIfAnyInputPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + all([resolve(1), reject(2), resolve(3)]) + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldAcceptAPromiseForAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2, 3])); + + all(resolve([1, 2, 3])) + ->then($mock); + } + + /** @test */ + public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([])); + + all(resolve(1)) + ->then($mock); + } + + /** @test */ + public function shouldPreserveTheOrderOfArrayWhenResolvingAsyncPromises() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2, 3])); + + $deferred = new Deferred(); + + all([resolve(1), $deferred->promise(), resolve(3)]) + ->then($mock); + + $deferred->resolve(2); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionAnyTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionAnyTest.php new file mode 100644 index 00000000..140b5512 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionAnyTest.php @@ -0,0 +1,204 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with( + $this->callback(function($exception){ + return $exception instanceof LengthException && + 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage(); + }) + ); + + any([]) + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldResolveToNullWithNonArrayInput() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + any(null) + ->then($mock); + } + + /** @test */ + public function shouldResolveWithAnInputValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + any([1, 2, 3]) + ->then($mock); + } + + /** @test */ + public function shouldResolveWithAPromisedInputValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + any([resolve(1), resolve(2), resolve(3)]) + ->then($mock); + } + + /** @test */ + public function shouldRejectWithAllRejectedInputValuesIfAllInputsAreRejected() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([0 => 1, 1 => 2, 2 => 3])); + + any([reject(1), reject(2), reject(3)]) + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldResolveWhenFirstInputPromiseResolves() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + any([resolve(1), reject(2), reject(3)]) + ->then($mock); + } + + /** @test */ + public function shouldAcceptAPromiseForAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + any(resolve([1, 2, 3])) + ->then($mock); + } + + /** @test */ + public function shouldResolveToNullArrayWhenInputPromiseDoesNotResolveToArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + any(resolve(1)) + ->then($mock); + } + + /** @test */ + public function shouldNotRelyOnArryIndexesWhenUnwrappingToASingleResolutionValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $d1 = new Deferred(); + $d2 = new Deferred(); + + any(['abc' => $d1->promise(), 1 => $d2->promise()]) + ->then($mock); + + $d2->resolve(2); + $d1->resolve(1); + } + + /** @test */ + public function shouldRejectWhenInputPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + any(reject()) + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldCancelInputPromise() + { + $mock = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock + ->expects($this->once()) + ->method('cancel'); + + any($mock)->cancel(); + } + + /** @test */ + public function shouldCancelInputArrayPromises() + { + $mock1 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock1 + ->expects($this->once()) + ->method('cancel'); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->once()) + ->method('cancel'); + + any([$mock1, $mock2])->cancel(); + } + + /** @test */ + public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfills() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + + $deferred = New Deferred($mock); + $deferred->resolve(); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->never()) + ->method('cancel'); + + some([$deferred->promise(), $mock2], 1)->cancel(); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionCheckTypehintTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionCheckTypehintTest.php new file mode 100644 index 00000000..8449bc1f --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionCheckTypehintTest.php @@ -0,0 +1,118 @@ +assertTrue(_checkTypehint(function (\InvalidArgumentException $e) { + }, new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint(function (\InvalidArgumentException $e) { + }, new \Exception())); + } + + /** @test */ + public function shouldAcceptFunctionStringCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithTypehint', new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint('React\Promise\testCallbackWithTypehint', new \Exception())); + } + + /** @test */ + public function shouldAcceptInvokableObjectCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint(new TestCallbackWithTypehintClass(), new \Exception())); + } + + /** @test */ + public function shouldAcceptObjectMethodCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \Exception())); + } + + /** @test */ + public function shouldAcceptStaticClassCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \Exception())); + } + + /** @test */ + public function shouldAcceptClosureCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) { + }, new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptFunctionStringCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithoutTypehint', new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptInvokableObjectCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptObjectMethodCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint([new TestCallbackWithoutTypehintClass(), 'testCallback'], new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptStaticClassCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithoutTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException())); + } +} + +function testCallbackWithTypehint(\InvalidArgumentException $e) +{ +} + +function testCallbackWithoutTypehint() +{ +} + +class TestCallbackWithTypehintClass +{ + public function __invoke(\InvalidArgumentException $e) + { + + } + + public function testCallback(\InvalidArgumentException $e) + { + + } + + public static function testCallbackStatic(\InvalidArgumentException $e) + { + + } +} + +class TestCallbackWithoutTypehintClass +{ + public function __invoke() + { + + } + + public function testCallback() + { + + } + + public static function testCallbackStatic() + { + + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionMapTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionMapTest.php new file mode 100644 index 00000000..1ea560a1 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionMapTest.php @@ -0,0 +1,198 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([2, 4, 6])); + + map( + [1, 2, 3], + $this->mapper() + )->then($mock); + } + + /** @test */ + public function shouldMapInputPromisesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([2, 4, 6])); + + map( + [resolve(1), resolve(2), resolve(3)], + $this->mapper() + )->then($mock); + } + + /** @test */ + public function shouldMapMixedInputArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([2, 4, 6])); + + map( + [1, resolve(2), 3], + $this->mapper() + )->then($mock); + } + + /** @test */ + public function shouldMapInputWhenMapperReturnsAPromise() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([2, 4, 6])); + + map( + [1, 2, 3], + $this->promiseMapper() + )->then($mock); + } + + /** @test */ + public function shouldAcceptAPromiseForAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([2, 4, 6])); + + map( + resolve([1, resolve(2), 3]), + $this->mapper() + )->then($mock); + } + + /** @test */ + public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([])); + + map( + resolve(1), + $this->mapper() + )->then($mock); + } + + /** @test */ + public function shouldPreserveTheOrderOfArrayWhenResolvingAsyncPromises() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([2, 4, 6])); + + $deferred = new Deferred(); + + map( + [resolve(1), $deferred->promise(), resolve(3)], + $this->mapper() + )->then($mock); + + $deferred->resolve(2); + } + + /** @test */ + public function shouldRejectWhenInputContainsRejection() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + map( + [resolve(1), reject(2), resolve(3)], + $this->mapper() + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldRejectWhenInputPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + map( + reject(), + $this->mapper() + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldCancelInputPromise() + { + $mock = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock + ->expects($this->once()) + ->method('cancel'); + + map( + $mock, + $this->mapper() + )->cancel(); + } + + /** @test */ + public function shouldCancelInputArrayPromises() + { + $mock1 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock1 + ->expects($this->once()) + ->method('cancel'); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->once()) + ->method('cancel'); + + map( + [$mock1, $mock2], + $this->mapper() + )->cancel(); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionRaceTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionRaceTest.php new file mode 100644 index 00000000..83770ec2 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionRaceTest.php @@ -0,0 +1,211 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + race( + [] + )->then($mock); + } + + /** @test */ + public function shouldResolveValuesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + race( + [1, 2, 3] + )->then($mock); + } + + /** @test */ + public function shouldResolvePromisesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $d1 = new Deferred(); + $d2 = new Deferred(); + $d3 = new Deferred(); + + race( + [$d1->promise(), $d2->promise(), $d3->promise()] + )->then($mock); + + $d2->resolve(2); + + $d1->resolve(1); + $d3->resolve(3); + } + + /** @test */ + public function shouldResolveSparseArrayInput() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + race( + [null, 1, null, 2, 3] + )->then($mock); + } + + /** @test */ + public function shouldRejectIfFirstSettledPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $d1 = new Deferred(); + $d2 = new Deferred(); + $d3 = new Deferred(); + + race( + [$d1->promise(), $d2->promise(), $d3->promise()] + )->then($this->expectCallableNever(), $mock); + + $d2->reject(2); + + $d1->resolve(1); + $d3->resolve(3); + } + + /** @test */ + public function shouldAcceptAPromiseForAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + race( + resolve([1, 2, 3]) + )->then($mock); + } + + /** @test */ + public function shouldResolveToNullWhenInputPromiseDoesNotResolveToArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + race( + resolve(1) + )->then($mock); + } + + /** @test */ + public function shouldRejectWhenInputPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + race( + reject() + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldCancelInputPromise() + { + $mock = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock + ->expects($this->once()) + ->method('cancel'); + + race($mock)->cancel(); + } + + /** @test */ + public function shouldCancelInputArrayPromises() + { + $mock1 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock1 + ->expects($this->once()) + ->method('cancel'); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->once()) + ->method('cancel'); + + race([$mock1, $mock2])->cancel(); + } + + /** @test */ + public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfills() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + $deferred = New Deferred($mock); + $deferred->resolve(); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->never()) + ->method('cancel'); + + race([$deferred->promise(), $mock2])->cancel(); + } + + /** @test */ + public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + $deferred = New Deferred($mock); + $deferred->reject(); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->never()) + ->method('cancel'); + + race([$deferred->promise(), $mock2])->cancel(); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionReduceTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionReduceTest.php new file mode 100644 index 00000000..8b43a878 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionReduceTest.php @@ -0,0 +1,347 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(6)); + + reduce( + [1, 2, 3], + $this->plus() + )->then($mock); + } + + /** @test */ + public function shouldReduceValuesWithInitialValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(7)); + + reduce( + [1, 2, 3], + $this->plus(), + 1 + )->then($mock); + } + + /** @test */ + public function shouldReduceValuesWithInitialPromise() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(7)); + + reduce( + [1, 2, 3], + $this->plus(), + resolve(1) + )->then($mock); + } + + /** @test */ + public function shouldReducePromisedValuesWithoutInitialValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(6)); + + reduce( + [resolve(1), resolve(2), resolve(3)], + $this->plus() + )->then($mock); + } + + /** @test */ + public function shouldReducePromisedValuesWithInitialValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(7)); + + reduce( + [resolve(1), resolve(2), resolve(3)], + $this->plus(), + 1 + )->then($mock); + } + + /** @test */ + public function shouldReducePromisedValuesWithInitialPromise() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(7)); + + reduce( + [resolve(1), resolve(2), resolve(3)], + $this->plus(), + resolve(1) + )->then($mock); + } + + /** @test */ + public function shouldReduceEmptyInputWithInitialValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + reduce( + [], + $this->plus(), + 1 + )->then($mock); + } + + /** @test */ + public function shouldReduceEmptyInputWithInitialPromise() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + reduce( + [], + $this->plus(), + resolve(1) + )->then($mock); + } + + /** @test */ + public function shouldRejectWhenInputContainsRejection() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + reduce( + [resolve(1), reject(2), resolve(3)], + $this->plus(), + resolve(1) + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldResolveWithNullWhenInputIsEmptyAndNoInitialValueOrPromiseProvided() + { + // Note: this is different from when.js's behavior! + // In when.reduce(), this rejects with a TypeError exception (following + // JavaScript's [].reduce behavior. + // We're following PHP's array_reduce behavior and resolve with NULL. + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + reduce( + [], + $this->plus() + )->then($mock); + } + + /** @test */ + public function shouldAllowSparseArrayInputWithoutInitialValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(3)); + + reduce( + [null, null, 1, null, 1, 1], + $this->plus() + )->then($mock); + } + + /** @test */ + public function shouldAllowSparseArrayInputWithInitialValue() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(4)); + + reduce( + [null, null, 1, null, 1, 1], + $this->plus(), + 1 + )->then($mock); + } + + /** @test */ + public function shouldReduceInInputOrder() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo('123')); + + reduce( + [1, 2, 3], + $this->append(), + '' + )->then($mock); + } + + /** @test */ + public function shouldAcceptAPromiseForAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo('123')); + + reduce( + resolve([1, 2, 3]), + $this->append(), + '' + )->then($mock); + } + + /** @test */ + public function shouldResolveToInitialValueWhenInputPromiseDoesNotResolveToAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + reduce( + resolve(1), + $this->plus(), + 1 + )->then($mock); + } + + /** @test */ + public function shouldProvideCorrectBasisValue() + { + $insertIntoArray = function ($arr, $val, $i) { + $arr[$i] = $val; + + return $arr; + }; + + $d1 = new Deferred(); + $d2 = new Deferred(); + $d3 = new Deferred(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2, 3])); + + reduce( + [$d1->promise(), $d2->promise(), $d3->promise()], + $insertIntoArray, + [] + )->then($mock); + + $d3->resolve(3); + $d1->resolve(1); + $d2->resolve(2); + } + + /** @test */ + public function shouldRejectWhenInputPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + reduce( + reject(), + $this->plus(), + 1 + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldCancelInputPromise() + { + $mock = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock + ->expects($this->once()) + ->method('cancel'); + + reduce( + $mock, + $this->plus(), + 1 + )->cancel(); + } + + /** @test */ + public function shouldCancelInputArrayPromises() + { + $mock1 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock1 + ->expects($this->once()) + ->method('cancel'); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->once()) + ->method('cancel'); + + reduce( + [$mock1, $mock2], + $this->plus(), + 1 + )->cancel(); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionRejectTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionRejectTest.php new file mode 100644 index 00000000..84b8ec6a --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionRejectTest.php @@ -0,0 +1,64 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($expected)); + + reject($expected) + ->then( + $this->expectCallableNever(), + $mock + ); + } + + /** @test */ + public function shouldRejectAFulfilledPromise() + { + $expected = 123; + + $resolved = new FulfilledPromise($expected); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($expected)); + + reject($resolved) + ->then( + $this->expectCallableNever(), + $mock + ); + } + + /** @test */ + public function shouldRejectARejectedPromise() + { + $expected = 123; + + $resolved = new RejectedPromise($expected); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($expected)); + + reject($resolved) + ->then( + $this->expectCallableNever(), + $mock + ); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionResolveTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionResolveTest.php new file mode 100644 index 00000000..53126bc0 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionResolveTest.php @@ -0,0 +1,171 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($expected)); + + resolve($expected) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldResolveAFulfilledPromise() + { + $expected = 123; + + $resolved = new FulfilledPromise($expected); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($expected)); + + resolve($resolved) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldResolveAThenable() + { + $thenable = new SimpleFulfilledTestThenable(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo('foo')); + + resolve($thenable) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldResolveACancellableThenable() + { + $thenable = new SimpleTestCancellableThenable(); + + $promise = resolve($thenable); + $promise->cancel(); + + $this->assertTrue($thenable->cancelCalled); + } + + /** @test */ + public function shouldRejectARejectedPromise() + { + $expected = 123; + + $resolved = new RejectedPromise($expected); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($expected)); + + resolve($resolved) + ->then( + $this->expectCallableNever(), + $mock + ); + } + + /** @test */ + public function shouldSupportDeepNestingInPromiseChains() + { + $d = new Deferred(); + $d->resolve(false); + + $result = resolve(resolve($d->promise()->then(function ($val) { + $d = new Deferred(); + $d->resolve($val); + + $identity = function ($val) { + return $val; + }; + + return resolve($d->promise()->then($identity))->then( + function ($val) { + return !$val; + } + ); + }))); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(true)); + + $result->then($mock); + } + + /** @test */ + public function shouldSupportVeryDeepNestedPromises() + { + $deferreds = []; + + // @TODO Increase count once global-queue is merged + for ($i = 0; $i < 10; $i++) { + $deferreds[] = $d = new Deferred(); + $p = $d->promise(); + + $last = $p; + for ($j = 0; $j < 10; $j++) { + $last = $last->then(function($result) { + return $result; + }); + } + } + + $p = null; + foreach ($deferreds as $d) { + if ($p) { + $d->resolve($p); + } + + $p = $d->promise(); + } + + $deferreds[0]->resolve(true); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(true)); + + $deferreds[0]->promise()->then($mock); + } + + /** @test */ + public function returnsExtendePromiseForSimplePromise() + { + $promise = $this + ->getMockBuilder('React\Promise\PromiseInterface') + ->getMock(); + + $this->assertInstanceOf('React\Promise\ExtendedPromiseInterface', resolve($promise)); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/FunctionSomeTest.php b/lib/Ebanx/vendor/react/promise/tests/FunctionSomeTest.php new file mode 100644 index 00000000..276b54bb --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/FunctionSomeTest.php @@ -0,0 +1,258 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with( + $this->callback(function($exception){ + return $exception instanceof LengthException && + 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage(); + }) + ); + + some( + [], + 1 + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldRejectWithLengthExceptionWithInputArrayContainingNotEnoughItems() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with( + $this->callback(function($exception){ + return $exception instanceof LengthException && + 'Input array must contain at least 4 items but contains only 3 items.' === $exception->getMessage(); + }) + ); + + some( + [1, 2, 3], + 4 + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldResolveToEmptyArrayWithNonArrayInput() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([])); + + some( + null, + 1 + )->then($mock); + } + + /** @test */ + public function shouldResolveValuesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2])); + + some( + [1, 2, 3], + 2 + )->then($mock); + } + + /** @test */ + public function shouldResolvePromisesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2])); + + some( + [resolve(1), resolve(2), resolve(3)], + 2 + )->then($mock); + } + + /** @test */ + public function shouldResolveSparseArrayInput() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([null, 1])); + + some( + [null, 1, null, 2, 3], + 2 + )->then($mock); + } + + /** @test */ + public function shouldRejectIfAnyInputPromiseRejectsBeforeDesiredNumberOfInputsAreResolved() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1 => 2, 2 => 3])); + + some( + [resolve(1), reject(2), reject(3)], + 2 + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldAcceptAPromiseForAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([1, 2])); + + some( + resolve([1, 2, 3]), + 2 + )->then($mock); + } + + /** @test */ + public function shouldResolveWithEmptyArrayIfHowManyIsLessThanOne() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([])); + + some( + [1], + 0 + )->then($mock); + } + + /** @test */ + public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([])); + + some( + resolve(1), + 1 + )->then($mock); + } + + /** @test */ + public function shouldRejectWhenInputPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + some( + reject(), + 1 + )->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldCancelInputPromise() + { + $mock = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock + ->expects($this->once()) + ->method('cancel'); + + some($mock, 1)->cancel(); + } + + /** @test */ + public function shouldCancelInputArrayPromises() + { + $mock1 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock1 + ->expects($this->once()) + ->method('cancel'); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->once()) + ->method('cancel'); + + some([$mock1, $mock2], 1)->cancel(); + } + + /** @test */ + public function shouldNotCancelOtherPendingInputArrayPromisesIfEnoughPromisesFulfill() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + $deferred = New Deferred($mock); + $deferred->resolve(); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->never()) + ->method('cancel'); + + some([$deferred->promise(), $mock2], 1); + } + + /** @test */ + public function shouldNotCancelOtherPendingInputArrayPromisesIfEnoughPromisesReject() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + $deferred = New Deferred($mock); + $deferred->reject(); + + $mock2 = $this + ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMock(); + $mock2 + ->expects($this->never()) + ->method('cancel'); + + some([$deferred->promise(), $mock2], 2); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/LazyPromiseTest.php b/lib/Ebanx/vendor/react/promise/tests/LazyPromiseTest.php new file mode 100644 index 00000000..b6308818 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/LazyPromiseTest.php @@ -0,0 +1,107 @@ +promise(); + }; + + return new CallbackPromiseAdapter([ + 'promise' => function () use ($factory) { + return new LazyPromise($factory); + }, + 'resolve' => [$d, 'resolve'], + 'reject' => [$d, 'reject'], + 'notify' => [$d, 'progress'], + 'settle' => [$d, 'resolve'], + ]); + } + + /** @test */ + public function shouldNotCallFactoryIfThenIsNotInvoked() + { + $factory = $this->createCallableMock(); + $factory + ->expects($this->never()) + ->method('__invoke'); + + new LazyPromise($factory); + } + + /** @test */ + public function shouldCallFactoryIfThenIsInvoked() + { + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke'); + + $p = new LazyPromise($factory); + $p->then(); + } + + /** @test */ + public function shouldReturnPromiseFromFactory() + { + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnValue(new FulfilledPromise(1))); + + $onFulfilled = $this->createCallableMock(); + $onFulfilled + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $p = new LazyPromise($factory); + + $p->then($onFulfilled); + } + + /** @test */ + public function shouldReturnPromiseIfFactoryReturnsNull() + { + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnValue(null)); + + $p = new LazyPromise($factory); + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then()); + } + + /** @test */ + public function shouldReturnRejectedPromiseIfFactoryThrowsException() + { + $exception = new \Exception(); + + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke') + ->will($this->throwException($exception)); + + $onRejected = $this->createCallableMock(); + $onRejected + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $p = new LazyPromise($factory); + + $p->then($this->expectCallableNever(), $onRejected); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php b/lib/Ebanx/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php new file mode 100644 index 00000000..bdedf465 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php @@ -0,0 +1,40 @@ +callbacks = $callbacks; + } + + public function promise() + { + return call_user_func_array($this->callbacks['promise'], func_get_args()); + } + + public function resolve() + { + return call_user_func_array($this->callbacks['resolve'], func_get_args()); + } + + public function reject() + { + return call_user_func_array($this->callbacks['reject'], func_get_args()); + } + + public function notify() + { + return call_user_func_array($this->callbacks['notify'], func_get_args()); + } + + public function settle() + { + return call_user_func_array($this->callbacks['settle'], func_get_args()); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php b/lib/Ebanx/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php new file mode 100644 index 00000000..9157cd4e --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php @@ -0,0 +1,14 @@ + function () use ($promise) { + return $promise; + }, + 'resolve' => $resolveCallback, + 'reject' => $rejectCallback, + 'notify' => $progressCallback, + 'settle' => $resolveCallback, + ]); + } + + /** @test */ + public function shouldRejectIfResolverThrowsException() + { + $exception = new \Exception('foo'); + + $promise = new Promise(function () use ($exception) { + throw $exception; + }); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $promise + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldFulfillIfFullfilledWithSimplePromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo('foo')); + + $adapter->promise() + ->then($mock); + + $adapter->resolve(new SimpleFulfilledTestPromise()); + } + + /** @test */ + public function shouldRejectIfRejectedWithSimplePromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo('foo')); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->resolve(new SimpleRejectedTestPromise()); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php new file mode 100644 index 00000000..0e2b8883 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php @@ -0,0 +1,299 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->isType('callable'), $this->isType('callable'), $this->isType('callable')); + + $adapter = $this->getPromiseTestAdapter($mock); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldFulfillPromiseIfCancellerFulfills() + { + $adapter = $this->getPromiseTestAdapter(function ($resolve) { + $resolve(1); + }); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($mock, $this->expectCallableNever()); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldRejectPromiseIfCancellerRejects() + { + $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject) { + $reject(1); + }); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldRejectPromiseWithExceptionIfCancellerThrows() + { + $e = new \Exception(); + + $adapter = $this->getPromiseTestAdapter(function () use ($e) { + throw $e; + }); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($e)); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldProgressPromiseIfCancellerNotifies() + { + $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject, $progress) { + $progress(1); + }); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldCallCancellerOnlyOnceIfCancellerResolves() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnCallback(function ($resolve) { + $resolve(); + })); + + $adapter = $this->getPromiseTestAdapter($mock); + + $adapter->promise()->cancel(); + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldHaveNoEffectIfCancellerDoesNothing() + { + $adapter = $this->getPromiseTestAdapter(function () {}); + + $adapter->promise() + ->then($this->expectCallableNever(), $this->expectCallableNever()); + + $adapter->promise()->cancel(); + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldCallCancellerFromDeepNestedPromiseChain() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke'); + + $adapter = $this->getPromiseTestAdapter($mock); + + $promise = $adapter->promise() + ->then(function () { + return new Promise\Promise(function () {}); + }) + ->then(function () { + $d = new Promise\Deferred(); + + return $d->promise(); + }) + ->then(function () { + return new Promise\Promise(function () {}); + }); + + $promise->cancel(); + } + + /** @test */ + public function cancelCalledOnChildrenSouldOnlyCancelWhenAllChildrenCancelled() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); + + $child1 = $adapter->promise() + ->then() + ->then(); + + $adapter->promise() + ->then(); + + $child1->cancel(); + } + + /** @test */ + public function cancelShouldTriggerCancellerWhenAllChildrenCancel() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $child1 = $adapter->promise() + ->then() + ->then(); + + $child2 = $adapter->promise() + ->then(); + + $child1->cancel(); + $child2->cancel(); + } + + /** @test */ + public function cancelShouldNotTriggerCancellerWhenCancellingOneChildrenMultipleTimes() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); + + $child1 = $adapter->promise() + ->then() + ->then(); + + $child2 = $adapter->promise() + ->then(); + + $child1->cancel(); + $child1->cancel(); + } + + /** @test */ + public function cancelShouldTriggerCancellerOnlyOnceWhenCancellingMultipleTimes() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $adapter->promise()->cancel(); + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldAlwaysTriggerCancellerWhenCalledOnRootPromise() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $adapter->promise() + ->then() + ->then(); + + $adapter->promise() + ->then(); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function cancelShouldTriggerCancellerWhenFollowerCancels() + { + $adapter1 = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $root = $adapter1->promise(); + + $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $follower = $adapter2->promise(); + $adapter2->resolve($root); + + $follower->cancel(); + } + + /** @test */ + public function cancelShouldNotTriggerCancellerWhenCancellingOnlyOneFollower() + { + $adapter1 = $this->getPromiseTestAdapter($this->expectCallableNever()); + + $root = $adapter1->promise(); + + $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $follower1 = $adapter2->promise(); + $adapter2->resolve($root); + + $adapter3 = $this->getPromiseTestAdapter($this->expectCallableNever()); + $adapter3->resolve($root); + + $follower1->cancel(); + } + + /** @test */ + public function cancelCalledOnFollowerShouldOnlyCancelWhenAllChildrenAndFollowerCancelled() + { + $adapter1 = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $root = $adapter1->promise(); + + $child = $root->then(); + + $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $follower = $adapter2->promise(); + $adapter2->resolve($root); + + $follower->cancel(); + $child->cancel(); + } + + /** @test */ + public function cancelShouldNotTriggerCancellerWhenCancellingFollowerButNotChildren() + { + $adapter1 = $this->getPromiseTestAdapter($this->expectCallableNever()); + + $root = $adapter1->promise(); + + $root->then(); + + $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce()); + + $follower = $adapter2->promise(); + $adapter2->resolve($root); + + $follower->cancel(); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/FullTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/FullTestTrait.php new file mode 100644 index 00000000..3ce45d61 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/FullTestTrait.php @@ -0,0 +1,15 @@ +getPromiseTestAdapter(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + $adapter->promise() + ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); + + $adapter->notify($sentinel); + } + + /** @test */ + public function notifyShouldPropagateProgressToDownstreamPromises() + { + $adapter = $this->getPromiseTestAdapter(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnArgument(0)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + $adapter->promise() + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock + ) + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock2 + ); + + $adapter->notify($sentinel); + } + + /** @test */ + public function notifyShouldPropagateTransformedProgressToDownstreamPromises() + { + $adapter = $this->getPromiseTestAdapter(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnValue($sentinel)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + $adapter->promise() + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock + ) + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock2 + ); + + $adapter->notify(1); + } + + /** @test */ + public function notifyShouldPropagateCaughtExceptionValueAsProgress() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->throwException($exception)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock + ) + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock2 + ); + + $adapter->notify(1); + } + + /** @test */ + public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise() + { + $adapter = $this->getPromiseTestAdapter(); + $adapter2 = $this->getPromiseTestAdapter(); + + $promise2 = $adapter2->promise(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + // resolve BEFORE attaching progress handler + $adapter->resolve(); + + $adapter->promise() + ->then(function () use ($promise2) { + return $promise2; + }) + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock + ); + + $adapter2->notify($sentinel); + } + + /** @test */ + public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise() + { + $adapter = $this->getPromiseTestAdapter(); + $adapter2 = $this->getPromiseTestAdapter(); + + $promise2 = $adapter2->promise(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + $adapter->promise() + ->then(function () use ($promise2) { + return $promise2; + }) + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock + ); + + // resolve AFTER attaching progress handler + $adapter->resolve(); + $adapter2->notify($sentinel); + } + + /** @test */ + public function notifyShouldForwardProgressWhenResolvedWithAnotherPromise() + { + $adapter = $this->getPromiseTestAdapter(); + $adapter2 = $this->getPromiseTestAdapter(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnValue($sentinel)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + $adapter->promise() + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock + ) + ->then( + $this->expectCallableNever(), + $this->expectCallableNever(), + $mock2 + ); + + $adapter->resolve($adapter2->promise()); + $adapter2->notify($sentinel); + } + + /** @test */ + public function notifyShouldAllowResolveAfterProgress() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->at(0)) + ->method('__invoke') + ->with($this->identicalTo(1)); + $mock + ->expects($this->at(1)) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->promise() + ->then( + $mock, + $this->expectCallableNever(), + $mock + ); + + $adapter->notify(1); + $adapter->resolve(2); + } + + /** @test */ + public function notifyShouldAllowRejectAfterProgress() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->at(0)) + ->method('__invoke') + ->with($this->identicalTo(1)); + $mock + ->expects($this->at(1)) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->promise() + ->then( + $this->expectCallableNever(), + $mock, + $mock + ); + + $adapter->notify(1); + $adapter->reject(2); + } + + /** @test */ + public function notifyShouldReturnSilentlyOnProgressWhenAlreadyRejected() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->reject(1); + + $this->assertNull($adapter->notify()); + } + + /** @test */ + public function notifyShouldInvokeProgressHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise()->progress($mock); + $adapter->notify(1); + } + + /** @test */ + public function notifyShouldInvokeProgressHandlerFromDone() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $this->assertNull($adapter->promise()->done(null, null, $mock)); + $adapter->notify(1); + } + + /** @test */ + public function notifyShouldThrowExceptionThrownProgressHandlerFromDone() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, null, function () { + throw new \Exception('UnhandledRejectionException'); + })); + $adapter->notify(1); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php new file mode 100644 index 00000000..428230b9 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php @@ -0,0 +1,351 @@ +getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->resolve(1); + $adapter->resolve(2); + + $adapter->promise() + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function fulfilledPromiseShouldInvokeNewlyAddedCallback() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->resolve(1); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($mock, $this->expectCallableNever()); + } + + /** @test */ + public function thenShouldForwardResultWhenCallbackIsNull() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->resolve(1); + $adapter->promise() + ->then( + null, + $this->expectCallableNever() + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function thenShouldForwardCallbackResultToNextCallback() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->resolve(1); + $adapter->promise() + ->then( + function ($val) { + return $val + 1; + }, + $this->expectCallableNever() + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function thenShouldForwardPromisedCallbackResultValueToNextCallback() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->resolve(1); + $adapter->promise() + ->then( + function ($val) { + return \React\Promise\resolve($val + 1); + }, + $this->expectCallableNever() + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackReturnsARejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->resolve(1); + $adapter->promise() + ->then( + function ($val) { + return \React\Promise\reject($val + 1); + }, + $this->expectCallableNever() + ) + ->then( + $this->expectCallableNever(), + $mock + ); + } + + /** @test */ + public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackThrows() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->throwException($exception)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->resolve(1); + $adapter->promise() + ->then( + $mock, + $this->expectCallableNever() + ) + ->then( + $this->expectCallableNever(), + $mock2 + ); + } + + /** @test */ + public function cancelShouldReturnNullForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->resolve(); + + $this->assertNull($adapter->promise()->cancel()); + } + + /** @test */ + public function cancelShouldHaveNoEffectForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); + + $adapter->resolve(); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function doneShouldInvokeFulfillmentHandlerForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->resolve(1); + $this->assertNull($adapter->promise()->done($mock)); + } + + /** @test */ + public function doneShouldThrowExceptionThrownFulfillmentHandlerForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->resolve(1); + $this->assertNull($adapter->promise()->done(function () { + throw new \Exception('UnhandledRejectionException'); + })); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejectsForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $adapter->resolve(1); + $this->assertNull($adapter->promise()->done(function () { + return \React\Promise\reject(); + })); + } + + /** @test */ + public function otherwiseShouldNotInvokeRejectionHandlerForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->resolve(1); + $adapter->promise()->otherwise($this->expectCallableNever()); + } + + /** @test */ + public function alwaysShouldNotSuppressValueForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->resolve($value); + $adapter->promise() + ->always(function () {}) + ->then($mock); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->resolve($value); + $adapter->promise() + ->always(function () { + return 1; + }) + ->then($mock); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->resolve($value); + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then($mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->resolve(1); + $adapter->promise() + ->always(function () use ($exception) { + throw $exception; + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->resolve(1); + $adapter->promise() + ->always(function () use ($exception) { + return \React\Promise\reject($exception); + }) + ->then(null, $mock); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php new file mode 100644 index 00000000..a4f48ee2 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php @@ -0,0 +1,68 @@ +getPromiseTestAdapter(); + + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then()); + } + + /** @test */ + public function thenShouldReturnAllowNullForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null)); + } + + /** @test */ + public function cancelShouldReturnNullForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertNull($adapter->promise()->cancel()); + } + + /** @test */ + public function doneShouldReturnNullForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertNull($adapter->promise()->done()); + } + + /** @test */ + public function doneShouldReturnAllowNullForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertNull($adapter->promise()->done(null, null, null)); + } + + /** @test */ + public function otherwiseShouldNotInvokeRejectionHandlerForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $adapter->promise()->otherwise($this->expectCallableNever()); + } + + /** @test */ + public function alwaysShouldReturnAPromiseForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {})); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php new file mode 100644 index 00000000..98d1dcf9 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php @@ -0,0 +1,512 @@ +getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->reject(1); + $adapter->reject(2); + + $adapter->promise() + ->then( + $this->expectCallableNever(), + $mock + ); + } + + /** @test */ + public function rejectedPromiseShouldInvokeNewlyAddedCallback() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->reject(1); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function shouldForwardUndefinedRejectionValue() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with(null); + + $adapter->reject(1); + $adapter->promise() + ->then( + $this->expectCallableNever(), + function () { + // Presence of rejection handler is enough to switch back + // to resolve mode, even though it returns undefined. + // The ONLY way to propagate a rejection is to re-throw or + // return a rejected promise; + } + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldSwitchFromErrbacksToCallbacksWhenErrbackDoesNotExplicitlyPropagate() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->reject(1); + $adapter->promise() + ->then( + $this->expectCallableNever(), + function ($val) { + return $val + 1; + } + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldSwitchFromErrbacksToCallbacksWhenErrbackReturnsAResolution() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->reject(1); + $adapter->promise() + ->then( + $this->expectCallableNever(), + function ($val) { + return \React\Promise\resolve($val + 1); + } + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldPropagateRejectionsWhenErrbackThrows() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->throwException($exception)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject(1); + $adapter->promise() + ->then( + $this->expectCallableNever(), + $mock + ) + ->then( + $this->expectCallableNever(), + $mock2 + ); + } + + /** @test */ + public function shouldPropagateRejectionsWhenErrbackReturnsARejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $adapter->reject(1); + $adapter->promise() + ->then( + $this->expectCallableNever(), + function ($val) { + return \React\Promise\reject($val + 1); + } + ) + ->then( + $this->expectCallableNever(), + $mock + ); + } + + /** @test */ + public function doneShouldInvokeRejectionHandlerForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, $mock)); + } + + /** @test */ + public function doneShouldThrowExceptionThrownByRejectionHandlerForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, function () { + throw new \Exception('UnhandledRejectionException'); + })); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done()); + } + + /** @test */ + public function unhandledRejectionExceptionThrownByDoneHoldsRejectionValue() + { + $adapter = $this->getPromiseTestAdapter(); + + $expected = new \stdClass(); + + $adapter->reject($expected); + + try { + $adapter->promise()->done(); + } catch (UnhandledRejectionException $e) { + $this->assertSame($expected, $e->getReason()); + return; + } + + $this->fail(); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejectsForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(); + })); + } + + /** @test */ + public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(new \Exception('UnhandledRejectionException')); + })); + } + + /** @test */ + public function doneShouldThrowExceptionProvidedAsRejectionValueForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->reject(new \Exception('UnhandledRejectionException')); + $this->assertNull($adapter->promise()->done()); + } + + /** @test */ + public function doneShouldThrowWithDeepNestingPromiseChainsForRejectedPromise() + { + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $exception = new \Exception('UnhandledRejectionException'); + + $d = new Deferred(); + $d->resolve(); + + $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) { + $d = new Deferred(); + $d->resolve(); + + return \React\Promise\resolve($d->promise()->then(function () {}))->then( + function () use ($exception) { + throw $exception; + } + ); + }))); + + $result->done(); + } + + /** @test */ + public function doneShouldRecoverWhenRejectionHandlerCatchesExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->reject(new \Exception('UnhandledRejectionException')); + $this->assertNull($adapter->promise()->done(null, function (\Exception $e) { + + })); + } + + /** @test */ + public function otherwiseShouldInvokeRejectionHandlerForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->reject(1); + $adapter->promise()->otherwise($mock); + } + + /** @test */ + public function otherwiseShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->otherwise(function ($reason) use ($mock) { + $mock($reason); + }); + } + + /** @test */ + public function otherwiseShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \InvalidArgumentException(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->otherwise(function (\InvalidArgumentException $reason) use ($mock) { + $mock($reason); + }); + } + + /** @test */ + public function otherwiseShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->expectCallableNever(); + + $adapter->reject($exception); + $adapter->promise() + ->otherwise(function (\InvalidArgumentException $reason) use ($mock) { + $mock($reason); + }); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->always(function () {}) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->always(function () { + return 1; + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception1 = new \Exception(); + $exception2 = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception2)); + + $adapter->reject($exception1); + $adapter->promise() + ->always(function () use ($exception2) { + throw $exception2; + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception1 = new \Exception(); + $exception2 = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception2)); + + $adapter->reject($exception1); + $adapter->promise() + ->always(function () use ($exception2) { + return \React\Promise\reject($exception2); + }) + ->then(null, $mock); + } + + /** @test */ + public function cancelShouldReturnNullForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->reject(); + + $this->assertNull($adapter->promise()->cancel()); + } + + /** @test */ + public function cancelShouldHaveNoEffectForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); + + $adapter->reject(); + + $adapter->promise()->cancel(); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php new file mode 100644 index 00000000..e363b6d9 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php @@ -0,0 +1,86 @@ +getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then()); + } + + /** @test */ + public function thenShouldReturnAllowNullForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null)); + } + + /** @test */ + public function cancelShouldReturnNullForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + + $this->assertNull($adapter->promise()->cancel()); + } + + /** @test */ + public function cancelShouldHaveNoEffectForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); + + $adapter->settle(); + + $adapter->promise()->cancel(); + } + + /** @test */ + public function doneShouldReturnNullForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertNull($adapter->promise()->done(null, function () {})); + } + + /** @test */ + public function doneShouldReturnAllowNullForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertNull($adapter->promise()->done(null, function () {}, null)); + } + + /** @test */ + public function progressShouldNotInvokeProgressHandlerForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $adapter->promise()->progress($this->expectCallableNever()); + $adapter->notify(); + } + + /** @test */ + public function alwaysShouldReturnAPromiseForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {})); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php new file mode 100644 index 00000000..063f178a --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php @@ -0,0 +1,368 @@ +getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->reject(1); + } + + /** @test */ + public function rejectShouldRejectWithFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->reject(Promise\resolve(1)); + } + + /** @test */ + public function rejectShouldRejectWithRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->reject(Promise\reject(1)); + } + + /** @test */ + public function rejectShouldForwardReasonWhenCallbackIsNull() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then( + $this->expectCallableNever() + ) + ->then( + $this->expectCallableNever(), + $mock + ); + + $adapter->reject(1); + } + + /** @test */ + public function rejectShouldMakePromiseImmutable() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then(null, function ($value) use ($adapter) { + $adapter->reject(3); + + return Promise\reject($value); + }) + ->then( + $this->expectCallableNever(), + $mock + ); + + $adapter->reject(1); + $adapter->reject(2); + } + + /** @test */ + public function notifyShouldInvokeOtherwiseHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->otherwise($mock); + + $adapter->reject(1); + } + + /** @test */ + public function doneShouldInvokeRejectionHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $this->assertNull($adapter->promise()->done(null, $mock)); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowExceptionThrownByRejectionHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, function () { + throw new \Exception('UnhandledRejectionException'); + })); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonException() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done()); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejects() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(); + })); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithException() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(new \Exception('UnhandledRejectionException')); + })); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRetunsPendingPromiseWhichRejectsLater() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $d = new Deferred(); + $promise = $d->promise(); + + $this->assertNull($adapter->promise()->done(null, function () use ($promise) { + return $promise; + })); + $adapter->reject(1); + $d->reject(1); + } + + /** @test */ + public function doneShouldThrowExceptionProvidedAsRejectionValue() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done()); + $adapter->reject(new \Exception('UnhandledRejectionException')); + } + + /** @test */ + public function doneShouldThrowWithDeepNestingPromiseChains() + { + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $exception = new \Exception('UnhandledRejectionException'); + + $d = new Deferred(); + + $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) { + $d = new Deferred(); + $d->resolve(); + + return \React\Promise\resolve($d->promise()->then(function () {}))->then( + function () use ($exception) { + throw $exception; + } + ); + }))); + + $result->done(); + + $d->resolve(); + } + + /** @test */ + public function doneShouldRecoverWhenRejectionHandlerCatchesException() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertNull($adapter->promise()->done(null, function (\Exception $e) { + + })); + $adapter->reject(new \Exception('UnhandledRejectionException')); + } + + /** @test */ + public function alwaysShouldNotSuppressRejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () {}) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () { + return 1; + }) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForRejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + throw $exception; + }) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForRejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + return \React\Promise\reject($exception); + }) + ->then(null, $mock); + + $adapter->reject($exception); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php new file mode 100644 index 00000000..0736d35a --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php @@ -0,0 +1,312 @@ +getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($mock); + + $adapter->resolve(1); + } + + /** @test */ + public function resolveShouldResolveWithPromisedValue() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($mock); + + $adapter->resolve(Promise\resolve(1)); + } + + /** @test */ + public function resolveShouldRejectWhenResolvedWithRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->resolve(Promise\reject(1)); + } + + /** @test */ + public function resolveShouldForwardValueWhenCallbackIsNull() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then( + null, + $this->expectCallableNever() + ) + ->then( + $mock, + $this->expectCallableNever() + ); + + $adapter->resolve(1); + } + + /** @test */ + public function resolveShouldMakePromiseImmutable() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->then(function ($value) use ($adapter) { + $adapter->resolve(3); + + return $value; + }) + ->then( + $mock, + $this->expectCallableNever() + ); + + $adapter->resolve(1); + $adapter->resolve(2); + } + + /** + * @test + */ + public function resolveShouldRejectWhenResolvedWithItself() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with(new \LogicException('Cannot resolve a promise with itself.')); + + $adapter->promise() + ->then( + $this->expectCallableNever(), + $mock + ); + + $adapter->resolve($adapter->promise()); + } + + /** + * @test + */ + public function resolveShouldRejectWhenResolvedWithAPromiseWhichFollowsItself() + { + $adapter1 = $this->getPromiseTestAdapter(); + $adapter2 = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with(new \LogicException('Cannot resolve a promise with itself.')); + + $promise1 = $adapter1->promise(); + + $promise2 = $adapter2->promise(); + + $promise2->then( + $this->expectCallableNever(), + $mock + ); + + $adapter1->resolve($promise2); + $adapter2->resolve($promise1); + } + + /** @test */ + public function doneShouldInvokeFulfillmentHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $this->assertNull($adapter->promise()->done($mock)); + $adapter->resolve(1); + } + + /** @test */ + public function doneShouldThrowExceptionThrownFulfillmentHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(function () { + throw new \Exception('UnhandledRejectionException'); + })); + $adapter->resolve(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejects() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(function () { + return \React\Promise\reject(); + })); + $adapter->resolve(1); + } + + /** @test */ + public function alwaysShouldNotSuppressValue() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->promise() + ->always(function () {}) + ->then($mock); + + $adapter->resolve($value); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->promise() + ->always(function () { + return 1; + }) + ->then($mock); + + $adapter->resolve($value); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then($mock); + + $adapter->resolve($value); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForFulfillment() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + throw $exception; + }) + ->then(null, $mock); + + $adapter->resolve(1); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForFulfillment() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + return \React\Promise\reject($exception); + }) + ->then(null, $mock); + + $adapter->resolve(1); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/RejectedPromiseTest.php b/lib/Ebanx/vendor/react/promise/tests/RejectedPromiseTest.php new file mode 100644 index 00000000..c886b009 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/RejectedPromiseTest.php @@ -0,0 +1,50 @@ + function () use (&$promise) { + if (!$promise) { + throw new \LogicException('RejectedPromise must be rejected before obtaining the promise'); + } + + return $promise; + }, + 'resolve' => function () { + throw new \LogicException('You cannot call resolve() for React\Promise\RejectedPromise'); + }, + 'reject' => function ($reason = null) use (&$promise) { + if (!$promise) { + $promise = new RejectedPromise($reason); + } + }, + 'notify' => function () { + // no-op + }, + 'settle' => function ($reason = null) use (&$promise) { + if (!$promise) { + $promise = new RejectedPromise($reason); + } + }, + ]); + } + + /** @test */ + public function shouldThrowExceptionIfConstructedWithAPromise() + { + $this->setExpectedException('\InvalidArgumentException'); + + return new RejectedPromise(new RejectedPromise()); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/Stub/CallableStub.php b/lib/Ebanx/vendor/react/promise/tests/Stub/CallableStub.php new file mode 100644 index 00000000..01208933 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/Stub/CallableStub.php @@ -0,0 +1,10 @@ +createCallableMock(); + $mock + ->expects($this->exactly($amount)) + ->method('__invoke'); + + return $mock; + } + + public function expectCallableOnce() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke'); + + return $mock; + } + + public function expectCallableNever() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + return $mock; + } + + public function createCallableMock() + { + return $this + ->getMockBuilder('React\\Promise\Stub\CallableStub') + ->getMock(); + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/bootstrap.php b/lib/Ebanx/vendor/react/promise/tests/bootstrap.php new file mode 100644 index 00000000..9b7f872a --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/bootstrap.php @@ -0,0 +1,7 @@ +addPsr4('React\\Promise\\', __DIR__); diff --git a/lib/Ebanx/vendor/react/promise/tests/fixtures/SimpleFulfilledTestPromise.php b/lib/Ebanx/vendor/react/promise/tests/fixtures/SimpleFulfilledTestPromise.php new file mode 100644 index 00000000..ef4d5301 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/fixtures/SimpleFulfilledTestPromise.php @@ -0,0 +1,21 @@ +cancelCalled = true; + } +} diff --git a/lib/Ebanx/vendor/react/promise/tests/fixtures/SimpleTestCancellableThenable.php b/lib/Ebanx/vendor/react/promise/tests/fixtures/SimpleTestCancellableThenable.php new file mode 100644 index 00000000..c0f15933 --- /dev/null +++ b/lib/Ebanx/vendor/react/promise/tests/fixtures/SimpleTestCancellableThenable.php @@ -0,0 +1,18 @@ +cancelCalled = true; + } +} diff --git a/package.xml b/package.xml index 68498645..806bdc4a 100644 --- a/package.xml +++ b/package.xml @@ -1,69 +1,2 @@ - - Facebook_Ads_Toolbox - 2.2.1 - beta - Facebook License - community - - Easily set up a pixel and catalog to use advanced advertising products and drive sales on Facebook. - Marketing on Facebook helps your business build lasting relationships with people, find new customers and increase sales for your online store. With this Facebook ad extension, we make it easy to reach the people who matter to your business and track the results of your advertising across devices. _This extension will help you:_ - -**Reach the right people** -Set up the Facebook pixel to find new customers, optimize your ads for people likely to buy and reach people with relevant ads on Facebook after they've visited your website. - -**Show them the right products** -Connect your product catalog to Facebook to use dynamic ads. Reach shoppers when they're on Facebook with ads for the products they viewed on your website. - -**Measure the results of your ads** -When you have the Facebook pixel set up, you can use Facebook ads reporting to understand the sales and revenue that resulted from your ads. - - -_Many online retailers have found success using the Facebook pixel to track the performance of their ads and run dynamic ads:_ - - -“The ability to measure sales was the first sign that our business would be a success. Our first day of breaking 100-plus sales always sticks out. Point blank, our marketing plan is Facebook, Facebook, and more Facebook... Facebook is 100% the backbone of our customer acquisition efforts and it's been made even better with the improved Facebook pixel” — Ali Najafian, co-founder, Trendy Butler - - -“I'm thrilled with the results we've seen since launching dynamic ads. We saw a rise in conversions almost immediately after launch and have been able to scale the program at an impressive pace over the past 6 months. These ads have proven to be a key component of our marketing efforts” — Megan Lang, Digital Marketing Manager, Food52 - - -“With dynamic ads, Target has been able to easily engage consumers with highly relevant creative. The early results have exceeded expectations. Performance has been especially strong on mobile devices — an important and fast-growing area for Target — where we're seeing two times the conversion rate” — Kristi Argyilan, Senior Vice President, Media and Guest Engagement at Target - - - -_What's included?_ -**(a) Pixel installer** -Installing the Facebook pixel allows you to access the features below: - -* Conversion tracking: See how successful your ad is by seeing what happened as a direct result of your ad (including conversions and sales) -* Optimization: Show your ads to people most likely to take a specific action after clicking on them, like adding an item to their cart or making a purchase -* Remarketing: When people visit your website, reach them again and remind them of your business with a Facebook ad - -** (b) Product catalog integration** -Importing your product catalog to Facebook allows you to use dynamic ads. Dynamic ads look identical to other link ads or carousel-format ads that are available on Facebook. However, instead of individually creating an ad for each of your products, Facebook creates the ads for you and personalizes them for each of your customers. - -* Scale: Use dynamic ads to promote all your products without needing to create individual ads for each item -* Highly relevant: Show people ads for products they're interested in to increase the likelihood of a purchase -* Always-on: Set up your campaigns once and continually reach people with the right product at the right time -* Cross-device: Reach people with ads on any device they use, regardless of where they first see your products - - -_Installation Instructions_ - -1. Install the extension from Magento Connect -2. Go to your Admin Panel -3. Select Facebook Ads Extension -4. Click Get Started - - -Additional FAQ -https://www.facebook.com/help/532749253576163 - - Fix store-specific currency issue - Jordan Rogers-Smithjordanrsjordanrs@fb.comYu Liliyuhkliyuhk@fb.comDmitri Dranishnikovdmitriddmitrid@fb.comAdam Hoffahoffahoff@fb.com - 2017-04-11 - - - - 5.3.08.0.0 - +ebanx_gateway2.7.0stableApache Software License (ASL) 2.0communityEBANX gatewayEBANX payment gatewayLogsEBANXebanx_magentomagento@ebanx.com2018-04-165.4.07.1.0 diff --git a/skin/adminhtml/default/default/ebanx/css/chosen-sprite.png b/skin/adminhtml/default/default/ebanx/css/chosen-sprite.png new file mode 100644 index 00000000..3611ae4a Binary files /dev/null and b/skin/adminhtml/default/default/ebanx/css/chosen-sprite.png differ diff --git a/skin/adminhtml/default/default/ebanx/css/chosen-sprite@2x.png b/skin/adminhtml/default/default/ebanx/css/chosen-sprite@2x.png new file mode 100644 index 00000000..ffe4d7d1 Binary files /dev/null and b/skin/adminhtml/default/default/ebanx/css/chosen-sprite@2x.png differ diff --git a/skin/adminhtml/default/default/ebanx/css/chosen.css b/skin/adminhtml/default/default/ebanx/css/chosen.css new file mode 100644 index 00000000..d203a071 --- /dev/null +++ b/skin/adminhtml/default/default/ebanx/css/chosen.css @@ -0,0 +1,430 @@ +/* @group Base */ +.chosen-container { + position: relative; + display: inline-block; + vertical-align: middle; + font-size: 13px; + zoom: 1; + *display: inline; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +.chosen-container .chosen-drop { + position: absolute; + top: 100%; + left: -9999px; + z-index: 1010; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: 1px solid #aaa; + border-top: 0; + background: #fff; + box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15); +} +.chosen-container.chosen-with-drop .chosen-drop { + left: 0; +} +.chosen-container a { + cursor: pointer; +} + +/* @end */ +/* @group Single Chosen */ +.chosen-container-single .chosen-single { + position: relative; + display: block; + overflow: hidden; + padding: 0 0 0 8px; + height: 23px; + border: 1px solid #aaa; + border-radius: 5px; + background-color: #fff; + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4)); + background: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background-clip: padding-box; + box-shadow: 0 0 3px white inset, 0 1px 1px rgba(0, 0, 0, 0.1); + color: #444; + text-decoration: none; + white-space: nowrap; + line-height: 24px; +} +.chosen-container-single .chosen-default { + color: #999; +} +.chosen-container-single .chosen-single span { + display: block; + overflow: hidden; + margin-right: 26px; + text-overflow: ellipsis; + white-space: nowrap; +} +.chosen-container-single .chosen-single-with-deselect span { + margin-right: 38px; +} +.chosen-container-single .chosen-single abbr { + position: absolute; + top: 6px; + right: 26px; + display: block; + width: 12px; + height: 12px; + background: url('chosen-sprite.png') -42px 1px no-repeat; + font-size: 1px; +} +.chosen-container-single .chosen-single abbr:hover { + background-position: -42px -10px; +} +.chosen-container-single.chosen-disabled .chosen-single abbr:hover { + background-position: -42px -10px; +} +.chosen-container-single .chosen-single div { + position: absolute; + top: 0; + right: 0; + display: block; + width: 18px; + height: 100%; +} +.chosen-container-single .chosen-single div b { + display: block; + width: 100%; + height: 100%; + background: url('chosen-sprite.png') no-repeat 0px 2px; +} +.chosen-container-single .chosen-search { + position: relative; + z-index: 1010; + margin: 0; + padding: 3px 4px; + white-space: nowrap; +} +.chosen-container-single .chosen-search input[type="text"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 1px 0; + padding: 4px 20px 4px 5px; + width: 100%; + height: auto; + outline: 0; + border: 1px solid #aaa; + background: white url('chosen-sprite.png') no-repeat 100% -20px; + background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(#eeeeee 1%, #ffffff 15%); + font-size: 1em; + font-family: sans-serif; + line-height: normal; + border-radius: 0; +} +.chosen-container-single .chosen-drop { + margin-top: -1px; + border-radius: 0 0 4px 4px; + background-clip: padding-box; +} +.chosen-container-single.chosen-container-single-nosearch .chosen-search { + position: absolute; + left: -9999px; +} + +/* @end */ +/* @group Results */ +.chosen-container .chosen-results { + position: relative; + overflow-x: hidden; + overflow-y: auto; + margin: 0 4px 4px 0; + padding: 0 0 0 4px; + max-height: 240px; + -webkit-overflow-scrolling: touch; +} +.chosen-container .chosen-results li { + display: none; + margin: 0; + padding: 5px 6px; + list-style: none; + line-height: 15px; +} +.chosen-container .chosen-results li.active-result { + display: list-item; + cursor: pointer; +} +.chosen-container .chosen-results li.disabled-result { + display: list-item; + color: #ccc; + cursor: default; +} +.chosen-container .chosen-results li.highlighted { + background-color: #3875d7; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); + background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%); + background-image: -moz-linear-gradient(#3875d7 20%, #2a62bc 90%); + background-image: -o-linear-gradient(#3875d7 20%, #2a62bc 90%); + background-image: linear-gradient(#3875d7 20%, #2a62bc 90%); + color: #fff; +} +.chosen-container .chosen-results li.no-results { + display: list-item; + background: #f4f4f4; +} +.chosen-container .chosen-results li.group-result { + display: list-item; + font-weight: bold; + cursor: default; +} +.chosen-container .chosen-results li.group-option { + padding-left: 15px; +} +.chosen-container .chosen-results li em { + font-style: normal; + text-decoration: underline; +} + +/* @end */ +/* @group Multi Chosen */ +.chosen-container-multi .chosen-choices { + position: relative; + overflow: hidden; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 0; + padding: 0; + width: 100%; + height: auto !important; + height: 1%; + border: 1px solid #aaa; + background-color: #fff; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background-image: -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); + background-image: -moz-linear-gradient(#eeeeee 1%, #ffffff 15%); + background-image: -o-linear-gradient(#eeeeee 1%, #ffffff 15%); + background-image: linear-gradient(#eeeeee 1%, #ffffff 15%); + cursor: text; +} +.chosen-container-multi .chosen-choices li { + float: left; + list-style: none; +} +.chosen-container-multi .chosen-choices li.search-field { + margin: 0; + padding: 0; + white-space: nowrap; +} +.chosen-container-multi .chosen-choices li.search-field input[type="text"] { + margin: 1px 0; + padding: 5px; + height: 15px; + outline: 0; + border: 0 !important; + background: transparent !important; + box-shadow: none; + color: #666; + font-size: 100%; + font-family: sans-serif; + line-height: normal; + border-radius: 0; +} +.chosen-container-multi .chosen-choices li.search-field .default { + color: #999; +} +.chosen-container-multi .chosen-choices li.search-choice { + position: relative; + margin: 3px 0 3px 5px; + padding: 3px 20px 3px 5px; + border: 1px solid #aaa; + border-radius: 3px; + background-color: #e4e4e4; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-clip: padding-box; + box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05); + color: #333; + line-height: 13px; + cursor: default; +} +.chosen-container-multi .chosen-choices li.search-choice .search-choice-close { + position: absolute; + top: 4px; + right: 3px; + display: block; + width: 12px; + height: 12px; + background: url('chosen-sprite.png') -42px 1px no-repeat; + font-size: 1px; +} +.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover { + background-position: -42px -10px; +} +.chosen-container-multi .chosen-choices li.search-choice-disabled { + padding-right: 5px; + border: 1px solid #ccc; + background-color: #e4e4e4; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + color: #666; +} +.chosen-container-multi .chosen-choices li.search-choice-focus { + background: #d4d4d4; +} +.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close { + background-position: -42px -10px; +} +.chosen-container-multi .chosen-results { + margin: 0; + padding: 0; +} +.chosen-container-multi .chosen-drop .result-selected { + display: list-item; + color: #ccc; + cursor: default; +} + +/* @end */ +/* @group Active */ +.chosen-container-active .chosen-single { + border: 1px solid #5897fb; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); +} +.chosen-container-active.chosen-with-drop .chosen-single { + border: 1px solid #aaa; + -moz-border-radius-bottomright: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + border-bottom-left-radius: 0; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff)); + background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%); + background-image: -moz-linear-gradient(#eeeeee 20%, #ffffff 80%); + background-image: -o-linear-gradient(#eeeeee 20%, #ffffff 80%); + background-image: linear-gradient(#eeeeee 20%, #ffffff 80%); + box-shadow: 0 1px 0 #fff inset; +} +.chosen-container-active.chosen-with-drop .chosen-single div { + border-left: none; + background: transparent; +} +.chosen-container-active.chosen-with-drop .chosen-single div b { + background-position: -18px 2px; +} +.chosen-container-active .chosen-choices { + border: 1px solid #5897fb; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); +} +.chosen-container-active .chosen-choices li.search-field input[type="text"] { + color: #111 !important; +} + +/* @end */ +/* @group Disabled Support */ +.chosen-disabled { + opacity: 0.5 !important; + cursor: default; +} +.chosen-disabled .chosen-single { + cursor: default; +} +.chosen-disabled .chosen-choices .search-choice .search-choice-close { + cursor: default; +} + +/* @end */ +/* @group Right to Left */ +.chosen-rtl { + text-align: right; +} +.chosen-rtl .chosen-single { + overflow: visible; + padding: 0 8px 0 0; +} +.chosen-rtl .chosen-single span { + margin-right: 0; + margin-left: 26px; + direction: rtl; +} +.chosen-rtl .chosen-single-with-deselect span { + margin-left: 38px; +} +.chosen-rtl .chosen-single div { + right: auto; + left: 3px; +} +.chosen-rtl .chosen-single abbr { + right: auto; + left: 26px; +} +.chosen-rtl .chosen-choices li { + float: right; +} +.chosen-rtl .chosen-choices li.search-field input[type="text"] { + direction: rtl; +} +.chosen-rtl .chosen-choices li.search-choice { + margin: 3px 5px 3px 0; + padding: 3px 5px 3px 19px; +} +.chosen-rtl .chosen-choices li.search-choice .search-choice-close { + right: auto; + left: 4px; +} +.chosen-rtl.chosen-container-single-nosearch .chosen-search, +.chosen-rtl .chosen-drop { + left: 9999px; +} +.chosen-rtl.chosen-container-single .chosen-results { + margin: 0 0 4px 4px; + padding: 0 4px 0 0; +} +.chosen-rtl .chosen-results li.group-option { + padding-right: 15px; + padding-left: 0; +} +.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div { + border-right: none; +} +.chosen-rtl .chosen-search input[type="text"] { + padding: 4px 5px 4px 20px; + background: white url('chosen-sprite.png') no-repeat -30px -20px; + background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat -30px -20px, -moz-linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat -30px -20px, -o-linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat -30px -20px, linear-gradient(#eeeeee 1%, #ffffff 15%); + direction: rtl; +} +.chosen-rtl.chosen-container-single .chosen-single div b { + background-position: 6px 2px; +} +.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b { + background-position: -12px 2px; +} + +/* @end */ +/* @group Retina compatibility */ +@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) { + .chosen-rtl .chosen-search input[type="text"], + .chosen-container-single .chosen-single abbr, + .chosen-container-single .chosen-single div b, + .chosen-container-single .chosen-search input[type="text"], + .chosen-container-multi .chosen-choices .search-choice .search-choice-close, + .chosen-container .chosen-results-scroll-down span, + .chosen-container .chosen-results-scroll-up span { + background-image: url('chosen-sprite@2x.png') !important; + background-size: 52px 37px !important; + background-repeat: no-repeat !important; + } +} +/* @end */ diff --git a/skin/adminhtml/default/default/ebanx/css/chosen.overwrite.css b/skin/adminhtml/default/default/ebanx/css/chosen.overwrite.css new file mode 100644 index 00000000..9ca83587 --- /dev/null +++ b/skin/adminhtml/default/default/ebanx/css/chosen.overwrite.css @@ -0,0 +1,9 @@ +.chosen-container .chosen-results li.highlighted { + background-color: #feaa44; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #feaa44), color-stop(90%, #f56d07)); + background-image: -webkit-linear-gradient(#feaa44 20%, #f56d07 90%); + background-image: -moz-linear-gradient(#feaa44 20%, #f56d07 90%); + background-image: -o-linear-gradient(#feaa44 20%, #f56d07 90%); + background-image: linear-gradient(#feaa44 20%, #f56d07 90%); + color: #fff; +} diff --git a/skin/adminhtml/default/default/ebanx/images/logo.png b/skin/adminhtml/default/default/ebanx/images/logo.png new file mode 100644 index 00000000..f67e8118 Binary files /dev/null and b/skin/adminhtml/default/default/ebanx/images/logo.png differ diff --git a/skin/adminhtml/default/default/ebanx/js/chosen-ebanx.js b/skin/adminhtml/default/default/ebanx/js/chosen-ebanx.js new file mode 100644 index 00000000..f3229ff3 --- /dev/null +++ b/skin/adminhtml/default/default/ebanx/js/chosen-ebanx.js @@ -0,0 +1,2 @@ +"use strict";document.observe("dom:loaded",function(){var e=$$('#payment_ebanx_settings select[multiple], #payment_ebanx_settings select[id$="_status"]');e.length&&e.each(function(e){new Chosen(e,{placeholder_text:" ",no_results_text:"Press Enter to add",width:"270px"})})}); +//# sourceMappingURL=chosen-ebanx.js.map diff --git a/skin/adminhtml/default/default/ebanx/js/chosen-ebanx.js.map b/skin/adminhtml/default/default/ebanx/js/chosen-ebanx.js.map new file mode 100644 index 00000000..f0258c64 --- /dev/null +++ b/skin/adminhtml/default/default/ebanx/js/chosen-ebanx.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["adminhtml/default/default/ebanx/js/chosen-ebanx.js"],"names":["document","observe","ebanxSelectFields","$$","length","no_results_text","placeholder_text"],"mappings":"aACAA,SAAAC,QAAA,aAAA,WAGE,IAAMC,EAAoBC,GAAG,2FAD/BH,EAAiBI,QACfF,EAAMA,KAAoBC,SAAAA,GACtBD,IAAAA,OAAAA,GACFA,iBAAA,IACEG,gBAAe,qBACbC,MAAAA","file":"chosen-ebanx.js","sourcesContent":["/* global $$ */\n/* global Chosen */\n\ndocument.observe('dom:loaded', () => {\n const ebanxSelectFields = $$('#payment_ebanx_settings select[multiple], #payment_ebanx_settings select[id$=\"_status\"]');\n if (ebanxSelectFields.length) {\n ebanxSelectFields.each((el) => {\n new Chosen(el, {\n placeholder_text: ' ',\n no_results_text: 'Press Enter to add',\n width: '270px',\n });\n });\n }\n});\n"]} \ No newline at end of file diff --git a/skin/adminhtml/default/default/ebanx/js/iof-option.js b/skin/adminhtml/default/default/ebanx/js/iof-option.js new file mode 100644 index 00000000..4c364072 --- /dev/null +++ b/skin/adminhtml/default/default/ebanx/js/iof-option.js @@ -0,0 +1,2 @@ +"use strict";document.observe("dom:loaded",function(){var e=document.querySelector("#payment_ebanx_settings_iof_local_amount");if(e){e.addEventListener("change",function(e){confirm("You need to validate this change with EBANX, only deselecting or selecting the box will not set this to your customer. Contact your EBANX Account Manager or Business Development Expert.")||(e.target.value=1-e.target.value)})}}); +//# sourceMappingURL=iof-option.js.map diff --git a/skin/adminhtml/default/default/ebanx/js/iof-option.js.map b/skin/adminhtml/default/default/ebanx/js/iof-option.js.map new file mode 100644 index 00000000..21dfd979 --- /dev/null +++ b/skin/adminhtml/default/default/ebanx/js/iof-option.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["adminhtml/default/default/ebanx/js/iof-option.js"],"names":["document","observe","iofLocalAmount","querySelector","addEventListener","el","confirm","confirmed"],"mappings":"aAAAA,SAAAC,QAAA,aAAA,WAGE,IAAMC,EAAiBF,SAASG,cAAc,4CADhDH,GAAAA,EAAAA,CAWKE,EAAAE,iBAAA,SANH,SAAAC,GAGcC,QAAAA,+LAAZC,EAAAA,OAAYD,MAAAA,EAAAA,EAAAA,OAAAA","file":"iof-option.js","sourcesContent":["/* global confirm */\n\ndocument.observe('dom:loaded', () => {\n const iofLocalAmount = document.querySelector('#payment_ebanx_settings_iof_local_amount');\n if (!iofLocalAmount) {\n return;\n }\n let confirmed = null;\n\n const renderIofAlert = (el) => {\n confirmed = confirm(`You need to validate this change with EBANX, only deselecting or selecting the box will not set this to your customer. Contact your EBANX Account Manager or Business Development Expert.`); // eslint-disable-line\n if (!confirmed) {\n el.target.value = 1 - el.target.value;\n }\n };\n iofLocalAmount.addEventListener('change', renderIofAlert);\n});\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/badge-apple.png b/skin/frontend/base/default/ebanx/badge-apple.png new file mode 100644 index 00000000..0e8b7235 Binary files /dev/null and b/skin/frontend/base/default/ebanx/badge-apple.png differ diff --git a/skin/frontend/base/default/ebanx/badge-google.png b/skin/frontend/base/default/ebanx/badge-google.png new file mode 100644 index 00000000..83b67196 Binary files /dev/null and b/skin/frontend/base/default/ebanx/badge-google.png differ diff --git a/skin/frontend/base/default/ebanx/css/amscheckout.css b/skin/frontend/base/default/ebanx/css/amscheckout.css new file mode 100644 index 00000000..330ed5d6 --- /dev/null +++ b/skin/frontend/base/default/ebanx/css/amscheckout.css @@ -0,0 +1,39 @@ +.amscheckout input[type="text"]:hover, +.amscheckout input[type="text"]:focus, +.amscheckout input[type="text"]:focus:hover{ + background-repeat: no-repeat !important; + background-size: auto 70% !important; + background-position: center right 7px !important; +} + +.amscheckout .ebanx-payment-method input.input-text.visa.identified { + background-image: url(/skin/frontend/base/default/ebanx/icons/visa.png) !important; +} + +.amscheckout .ebanx-payment-method input.input-text.mastercard.identified { + background-image: url(/skin/frontend/base/default/ebanx/icons/mastercard.png) !important; +} + +.amscheckout .ebanx-payment-method input.input-text.amex.identified { + background-image: url(/skin/frontend/base/default/ebanx/icons/amex.png) !important; +} + +.amscheckout .ebanx-payment-method input.input-text.discover.identified { + background-image: url(/skin/frontend/base/default/ebanx/icons/discover.png) !important; +} + +.amscheckout .ebanx-payment-method input.input-text.dinersclub.identified { + background-image: url(/skin/frontend/base/default/ebanx/icons/dinersclub.png) !important; +} + +.amscheckout .ebanx-payment-method input.input-text.maestro.identified { + background-image: url(/skin/frontend/base/default/ebanx/icons/maestro.png) !important; +} + +.amscheckout .ebanx-payment-method input.input-text.elo.identified { + background-image: url(/skin/frontend/base/default/ebanx/icons/elo.png) !important; +} + +.amscheckout .ebanx-payment-method input.input-text.unknown { + background-image: url(/skin/frontend/base/default/ebanx/icons/unknown.png) !important; +} diff --git a/skin/frontend/base/default/ebanx/css/checkout.css b/skin/frontend/base/default/ebanx/css/checkout.css new file mode 100644 index 00000000..6ccfc5a2 --- /dev/null +++ b/skin/frontend/base/default/ebanx/css/checkout.css @@ -0,0 +1,88 @@ +.sp-methods .container-new-card .form-list { + padding-left: 0; +} + +.ebanx-payment-method input.input-text.identified, +.ebanx-payment-method input.input-text.unknown { + padding-right: 50px; + + background-repeat: no-repeat; + background-size: auto 70%; + background-position: center right 7px; +} + +.ebanx-payment-method input.input-text.visa.identified { + background-image: url(../icons/visa.png); +} + +.ebanx-payment-method input.input-text.mastercard.identified { + background-image: url(../icons/mastercard.png); +} + +.ebanx-payment-method input.input-text.amex.identified { + background-image: url(../icons/amex.png); +} + +.ebanx-payment-method input.input-text.discover.identified { + background-image: url(../icons/discover.png); +} + +.ebanx-payment-method input.input-text.dinersclub.identified { + background-image: url(../icons/dinersclub.png); +} + +.ebanx-payment-method input.input-text.maestro.identified { + background-image: url(../icons/maestro.png); +} + +.ebanx-payment-method input.input-text.elo.identified { + background-image: url(../icons/elo.png); +} + +.ebanx-payment-method input.input-text.unknown { + background-image: url(../icons/unknown.png); +} + +.ebanx-payment-method input.input-text#ebanx_cc_ar_cc_number { + background-image: none; +} + +.ebanx-credit-card-row:after { + content: ""; + display: table; + clear: both; +} + +.sp-methods .form-list.ebanx-payment-method .total-to-pay { + background-position: right bottom; + background-repeat: no-repeat; + padding: 10px 168px 10px 0; +} + +.sp-methods .form-list.ebanx-es-method .total-to-pay { + background-image: url(../ebanx-estas-pagando.png); + background-size: 168px 22px; +} + +.sp-methods .form-list.ebanx-br-method .total-to-pay { + background-image: url(../ebanx-pagando.png); + background-size: 136px 22px; + padding-right: 136px; +} + +.ebanx-method-icon { + height: 30px; + display: inline-block; + vertical-align: middle; +} + +.save-card, +.new-card { display: none; } + +input[type=radio]:checked ~ .save-card { + display: block; +} + +input[type=radio]:checked ~ .new-card { + display: block; +} diff --git a/skin/frontend/base/default/ebanx/css/oneclick.css b/skin/frontend/base/default/ebanx/css/oneclick.css new file mode 100644 index 00000000..d6fc5fc1 --- /dev/null +++ b/skin/frontend/base/default/ebanx/css/oneclick.css @@ -0,0 +1,118 @@ +#ebanx-one-click-container { + position: relative; + float: left; + width: 100%; +} + +.clearfix:after { + content: ''; + display: table; + clear: both; +} + +.ebanx-one-click-tooltip { + position: absolute; + top: 110%; + left: 0; + z-index: 100; + width: 350px; + padding: 15px; + background: #FFF; + border: 1px solid #EDEDED; + box-shadow: 0 2px 11px 0 rgba(0, 0, 0, 0.15); + border-radius: 3px; + opacity: 0; + visibility: hidden; + transition: all .2s; + transform: translateY(5px); +} + +.ebanx-one-click-tooltip.is-active { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.ebanx-one-click-tooltip:after, .ebanx-one-click-tooltip:before { + bottom: 100%; + left: 20%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} + +.ebanx-one-click-tooltip:after { + border-color: rgba(255, 255, 255, 0); + border-bottom-color: #FFF; + border-width: 10px; + margin-left: -10px; +} + +.ebanx-one-click-tooltip:before { + border-color: rgba(245, 2, 2, 0); + border-bottom-color: #EDEDED; + border-width: 12px; + margin-left: -12px; +} + +#ebanx-one-click-close-button { + position: absolute; + top: 15px; + right: 15px; + + width: 20px; + height: 20px; + padding: 0; + + border: 0; + background: url(../images/close-button.png) no-repeat center; + background-size: 20px; + border-radius: 50%; +} + +#ebanx-one-click-close-button:hover { + background: url(../images/close-button.png) no-repeat center; + background-size: 20px; + border-radius: 50%; + opacity: .5; +} + +.ebanx-one-click-tooltip p { + margin: 0; +} + +.ebanx-one-click-tooltip p.ebanx-one-click-address { + font-weight: bold; + margin-bottom: 20px; +} + +#ebanx-one-click-card-list { + list-style: none; +} + +.ebanx-credit-card-option > label > img { + float: left; +} + +.ebanx-credit-card-option { + margin: 20px 0; +} + +.ebanx-credit-card-option:last-child { + margin-bottom: 0; +} + +#ebanx-oneclick-pay-button { + margin-bottom: 0; +} + +.save-card { + display: none; +} + +input[type=radio]:checked ~ .save-card { + display: block; +} diff --git a/skin/frontend/base/default/ebanx/css/sandbox-alert.css b/skin/frontend/base/default/ebanx/css/sandbox-alert.css new file mode 100644 index 00000000..b39830aa --- /dev/null +++ b/skin/frontend/base/default/ebanx/css/sandbox-alert.css @@ -0,0 +1,60 @@ +.sandbox-alert-box { + max-width: 100%; + display: inline-block; + position: relative; + border: 1px solid #F3E39A; + border-radius: 2px; + padding: 15px 10px 10px 10px; + background-color: #FCF8BB; +} + +.sandbox-alert-icon { + position: absolute; + top: 50%; + transform: translateY(-50%); +} + +.sandbox-alert-text { + margin-left: 42px; + font-size: 12px; +} + +.sandbox-form-tag { + display: inline-block; + background-color: #FCF8BB; + border: 2px solid #F3E39A; + border-radius: 2px; + padding: 0 5px 0 5px; + position: relative; + color: #8E6007; + font-size: 12px; + margin-left: 14px; +} + +.sandbox-form-tag:before { + content:""; + position: absolute; + right: 100%; + width: 0; + height: 0; + top: 2px; + left: -8px; + border-top: 7px solid transparent; + border-right: 7px solid #F3E39A; + border-bottom: 7px solid transparent; +} + +.sandbox-form-tag:after { + content:""; + position: absolute; + right: 100%; + width: 0; + height: 0; + top: 3px; + left: -5px; + border-top: 6px solid transparent; + border-right: 6px solid #FCF8BB; + border-bottom: 6px solid transparent; +} + + diff --git a/skin/frontend/base/default/ebanx/css/thankyou-page.css b/skin/frontend/base/default/ebanx/css/thankyou-page.css new file mode 100644 index 00000000..7ce97833 --- /dev/null +++ b/skin/frontend/base/default/ebanx/css/thankyou-page.css @@ -0,0 +1,49 @@ +.ebanx-details { + margin: 30px 0; +} + +.ebanx-details p { + margin: 5px 0; + + line-height: 1.5; +} + +.ebanx-groups { + margin: 20px 0; + overflow: hidden; +} + +.ebanx-group { + margin: 10px 0; +} + +.ebanx-group { + width: calc(50% - 10px); + margin: 0; + float: left; +} + +.ebanx-group:last-child { + margin-left: 20px; +} + +.ebanx-group a, +.ebanx-group button { + width: 100%; + text-align: center; + display: block; +} + +.ebanx-group img { + max-width: 100%; + height: auto; + display: inline-block; +} + +.ebanx-badges .ebanx-group:first-child a { + text-align: right; +} + +.ebanx-badges .ebanx-group:last-child a { + text-align: left; +} diff --git a/skin/frontend/base/default/ebanx/ebanx-account.png b/skin/frontend/base/default/ebanx/ebanx-account.png new file mode 100644 index 00000000..f3dcaa40 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-account.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-baloto.png b/skin/frontend/base/default/ebanx/ebanx-baloto.png new file mode 100644 index 00000000..e1abfa85 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-baloto.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-banking-ticket.png b/skin/frontend/base/default/ebanx/ebanx-banking-ticket.png new file mode 100644 index 00000000..e7e17972 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-banking-ticket.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-credit-card-ar.png b/skin/frontend/base/default/ebanx/ebanx-credit-card-ar.png new file mode 100644 index 00000000..4cfc4c2c Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-credit-card-ar.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-credit-card-br.png b/skin/frontend/base/default/ebanx/ebanx-credit-card-br.png new file mode 100644 index 00000000..7b1fc7f9 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-credit-card-br.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-credit-card-co.png b/skin/frontend/base/default/ebanx/ebanx-credit-card-co.png new file mode 100644 index 00000000..22436fab Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-credit-card-co.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-credit-card-mx.png b/skin/frontend/base/default/ebanx/ebanx-credit-card-mx.png new file mode 100644 index 00000000..a065f3ce Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-credit-card-mx.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-cupon.png b/skin/frontend/base/default/ebanx/ebanx-cupon.png new file mode 100644 index 00000000..5f57125d Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-cupon.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-debit-card.png b/skin/frontend/base/default/ebanx/ebanx-debit-card.png new file mode 100644 index 00000000..86f0f363 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-debit-card.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-eft.png b/skin/frontend/base/default/ebanx/ebanx-eft.png new file mode 100644 index 00000000..40f15b9a Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-eft.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-estas-pagando.png b/skin/frontend/base/default/ebanx/ebanx-estas-pagando.png new file mode 100644 index 00000000..3af496ce Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-estas-pagando.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-multicaja.png b/skin/frontend/base/default/ebanx/ebanx-multicaja.png new file mode 100644 index 00000000..fd823e8c Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-multicaja.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-oxxo.png b/skin/frontend/base/default/ebanx/ebanx-oxxo.png new file mode 100644 index 00000000..6abef37e Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-oxxo.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-pagando.png b/skin/frontend/base/default/ebanx/ebanx-pagando.png new file mode 100644 index 00000000..bfd832b4 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-pagando.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-pagoefectivo.png b/skin/frontend/base/default/ebanx/ebanx-pagoefectivo.png new file mode 100644 index 00000000..3cb11d7c Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-pagoefectivo.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-pagofacil.png b/skin/frontend/base/default/ebanx/ebanx-pagofacil.png new file mode 100644 index 00000000..9cd96921 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-pagofacil.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-paying.png b/skin/frontend/base/default/ebanx/ebanx-paying.png new file mode 100644 index 00000000..76664c78 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-paying.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-rapipago.png b/skin/frontend/base/default/ebanx/ebanx-rapipago.png new file mode 100644 index 00000000..61b66ef1 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-rapipago.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-safetypay.png b/skin/frontend/base/default/ebanx/ebanx-safetypay.png new file mode 100644 index 00000000..86bcbdd7 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-safetypay.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-sencillito.png b/skin/frontend/base/default/ebanx/ebanx-sencillito.png new file mode 100644 index 00000000..1bb25b3b Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-sencillito.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-servipag.png b/skin/frontend/base/default/ebanx/ebanx-servipag.png new file mode 100644 index 00000000..e84b6653 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-servipag.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-spei.png b/skin/frontend/base/default/ebanx/ebanx-spei.png new file mode 100644 index 00000000..1c8b3f14 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-spei.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-tef.png b/skin/frontend/base/default/ebanx/ebanx-tef.png new file mode 100644 index 00000000..7e9d2dbe Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-tef.png differ diff --git a/skin/frontend/base/default/ebanx/ebanx-webpay.png b/skin/frontend/base/default/ebanx/ebanx-webpay.png new file mode 100644 index 00000000..d12aae68 Binary files /dev/null and b/skin/frontend/base/default/ebanx/ebanx-webpay.png differ diff --git a/skin/frontend/base/default/ebanx/icons/amex.png b/skin/frontend/base/default/ebanx/icons/amex.png new file mode 100644 index 00000000..2ed80067 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/amex.png differ diff --git a/skin/frontend/base/default/ebanx/icons/carnet.png b/skin/frontend/base/default/ebanx/icons/carnet.png new file mode 100644 index 00000000..bfa159f4 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/carnet.png differ diff --git a/skin/frontend/base/default/ebanx/icons/diners.png b/skin/frontend/base/default/ebanx/icons/diners.png new file mode 100644 index 00000000..d879ad41 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/diners.png differ diff --git a/skin/frontend/base/default/ebanx/icons/dinersclub.png b/skin/frontend/base/default/ebanx/icons/dinersclub.png new file mode 100644 index 00000000..d879ad41 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/dinersclub.png differ diff --git a/skin/frontend/base/default/ebanx/icons/discover.png b/skin/frontend/base/default/ebanx/icons/discover.png new file mode 100644 index 00000000..607dfa2a Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/discover.png differ diff --git a/skin/frontend/base/default/ebanx/icons/ebanx-account.png b/skin/frontend/base/default/ebanx/icons/ebanx-account.png new file mode 100644 index 00000000..8c982347 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/ebanx-account.png differ diff --git a/skin/frontend/base/default/ebanx/icons/elo.png b/skin/frontend/base/default/ebanx/icons/elo.png new file mode 100644 index 00000000..a7a5c9f4 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/elo.png differ diff --git a/skin/frontend/base/default/ebanx/icons/hipercard.png b/skin/frontend/base/default/ebanx/icons/hipercard.png new file mode 100644 index 00000000..0944b1c8 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/hipercard.png differ diff --git a/skin/frontend/base/default/ebanx/icons/maestro.png b/skin/frontend/base/default/ebanx/icons/maestro.png new file mode 100644 index 00000000..0603114b Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/maestro.png differ diff --git a/skin/frontend/base/default/ebanx/icons/mastercard.png b/skin/frontend/base/default/ebanx/icons/mastercard.png new file mode 100644 index 00000000..3dcc915c Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/mastercard.png differ diff --git a/skin/frontend/base/default/ebanx/icons/unknown.png b/skin/frontend/base/default/ebanx/icons/unknown.png new file mode 100644 index 00000000..ab637637 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/unknown.png differ diff --git a/skin/frontend/base/default/ebanx/icons/visa-debit.png b/skin/frontend/base/default/ebanx/icons/visa-debit.png new file mode 100644 index 00000000..42f17481 Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/visa-debit.png differ diff --git a/skin/frontend/base/default/ebanx/icons/visa.png b/skin/frontend/base/default/ebanx/icons/visa.png new file mode 100644 index 00000000..45be9f1f Binary files /dev/null and b/skin/frontend/base/default/ebanx/icons/visa.png differ diff --git a/skin/frontend/base/default/ebanx/icons/warning-icon.svg b/skin/frontend/base/default/ebanx/icons/warning-icon.svg new file mode 100644 index 00000000..e5135fbb --- /dev/null +++ b/skin/frontend/base/default/ebanx/icons/warning-icon.svg @@ -0,0 +1,16 @@ + + + + Group + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/images/close-button.png b/skin/frontend/base/default/ebanx/images/close-button.png new file mode 100644 index 00000000..9e706a5f Binary files /dev/null and b/skin/frontend/base/default/ebanx/images/close-button.png differ diff --git a/skin/frontend/base/default/ebanx/js/amscheckout-inputs.js b/skin/frontend/base/default/ebanx/js/amscheckout-inputs.js new file mode 100644 index 00000000..3e340fe4 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/amscheckout-inputs.js @@ -0,0 +1,2 @@ +"use strict";var amsCheckoutHandler=function(){return document.querySelector('label[for="billing\\:taxvat_number"]')}; +//# sourceMappingURL=amscheckout-inputs.js.map diff --git a/skin/frontend/base/default/ebanx/js/amscheckout-inputs.js.map b/skin/frontend/base/default/ebanx/js/amscheckout-inputs.js.map new file mode 100644 index 00000000..443de2ad --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/amscheckout-inputs.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/amscheckout-inputs.js"],"names":["amsCheckoutHandler"],"mappings":"aACA,IAAIA,mBAAqB,WAAzB,OAAIA,SAAAA,cAAqB","file":"amscheckout-inputs.js","sourcesContent":["\nvar amsCheckoutHandler = () => { // eslint-disable-line no-unused-vars\n return document.querySelector('label[for=\"billing\\\\:taxvat_number\"]');\n};\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/checkout.js b/skin/frontend/base/default/ebanx/js/checkout.js new file mode 100644 index 00000000..ac75447c --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/checkout.js @@ -0,0 +1,2 @@ +"use strict";var waitFor=function(e,n){var t=setInterval(function(){var r=e();if(null==r)return!1;clearInterval(t),n(r)},500)},hasClass=function(e,n){return(" "+e.className+" ").indexOf(" "+n+" ")>-1},resetValidations=function(e,n){Array.from(e.querySelectorAll(n)).forEach(function(e){e.classList.remove("required-entry","validation-failed","brand-required"),e.nextElementSibling&&hasClass(e.nextElementSibling,"validation-advice")&&(e.nextElementSibling.style.display="none")})},addRequiredClassToInputs=function(e,n,t,r){resetValidations(t,r),Array.from(e).forEach(function(e){e.classList.add(n),hasClass(e,"hidden-input-brand")&&(e.classList.add("brand-required"),e.classList.remove("required-entry"))})},validationFormListener=function(e,n){var t=".required-entry-input",r=".required-entry-select";Array.from(n).forEach(function(n){n.querySelector("input[type=radio]").addEventListener("change",function(n){addRequiredClassToInputs(n.target.parentElement.querySelectorAll(t),"required-entry",e,t),addRequiredClassToInputs(n.target.parentElement.querySelectorAll(r),"validate-select",e,r)})})},initCreditCardOption=function(e,n){var t=e.querySelector("input[type=radio]"),r=".required-entry-input";t.checked=!0,addRequiredClassToInputs(t.parentElement.querySelectorAll(r),"required-entry",n,r)},initCreditCardWithoutSavedCards=function(e){e.querySelectorAll(".required-entry-input").forEach(function(e){e.classList.add("required-entry")}),e.querySelectorAll(".required-entry-select").forEach(function(e){e.classList.add("validate-select")})},initCreditCardForm=function(e,n){0!==e.length?(validationFormListener(n,e),initCreditCardOption(e[0],n)):initCreditCardWithoutSavedCards(n)},handleEbanxForm=function(e,n,t){waitFor(function(){return document.querySelector("#"+t)},function(e){var n=e.querySelectorAll(".ebanx-credit-card-option");initCreditCardForm(n,e)});var r=function(e){return document.getElementById(e)},a=null,i=r("ebanx_"+n+"_"+e+"_"+n+"_name"),d=r("ebanx_"+n+"_"+e+"_"+n+"_number"),u=r("ebanx_"+n+"_"+e+"_expiration"),l=r("ebanx_"+n+"_"+e+"_expiration_yr"),o=r("ebanx_"+n+"_"+e+"_"+n+"_cid"),s=r("ebanx_"+n+"_"+e+"_token"),c=r("ebanx_"+n+"_"+e+"_brand"),v=r("ebanx_"+n+"_"+e+"_masked_card_number"),_=r("ebanx_"+n+"_"+e+"_device_fingerprint"),f=r("ebanx_"+n+"_"+e+"_mode"),m=r("ebanx_"+n+"_"+e+"_integration_key"),y=r("ebanx_"+n+"_"+e+"_country"),b=r("ebanx-error-message"),p=void 0!==r("payment_form_ebanx_"+n+"_"+e),E="sandbox"===f.value?"test":"production",g=null;EBANX.config.setMode(E),EBANX.config.setPublishableKey(m.value),EBANX.config.setCountry(y.value);var h=function(e){var n=document.querySelector("#review-buttons-container > button");void 0!==n&&n&&(n.disabled=e)},x=function(e){if(!e.data.hasOwnProperty("status")){var n=e.error.err,t=n.message;return n.message||(EBANX.errors.InvalidValueFieldError(n.status_code),t=EBANX.errors.message||"Some error happened. Please, verify the data of your credit card and try again."),b.innerHTML=t,h(!1),setTimeout(function(){Validation.showAdvice({advices:!1},b,"ebanx-error-message")},500),!1}a=e.data,s.value=a.token,c.value=a.payment_type_code,v.value=a.masked_card_number,_.value=a.deviceId,h(!1),function(e){if(!e)return!1;var n=document.createEvent("Event");n.initEvent("click",!0,!0),e.dispatchEvent(n)}(g)},q=function(e){var n;d.value.length&&i.value.length&&u.value.length&&l.value.length&&o.value.length&&(!(n=e.relatedTarget)||"button"!==n.type&&"span"!==n.type||(g=n),a||(h(!0),EBANX.card.createToken({card_number:parseInt(d.value.replace(/ /g,"")),card_name:i.value,card_due_date:(parseInt(u.value)||0)+"/"+(parseInt(l.value)||0),card_cvv:o.value},x)))},C=function(){a=null,s.value="",c.value="",v.value="",_.value="",Validation.hideAdvice({advices:!1},b)};p&&(i.addEventListener("blur",q,!1),d.addEventListener("blur",q,!1),u.addEventListener("blur",q,!1),l.addEventListener("blur",q,!1),o.addEventListener("blur",q,!1),i.addEventListener("change",C,!1),d.addEventListener("change",C,!1),u.addEventListener("change",C,!1),l.addEventListener("change",C,!1),o.addEventListener("change",C,!1)),d.addEventListener("input",function(e){setInterval(function(){o.setAttribute("maxlength",3),(" "+e.target.className+" ").indexOf(" amex ")>-1&&o.setAttribute("maxlength",4),(" "+e.target.className+" ").indexOf(" unknown ")>-1&&(o.value="")},200)})}; +//# sourceMappingURL=checkout.js.map diff --git a/skin/frontend/base/default/ebanx/js/checkout.js.map b/skin/frontend/base/default/ebanx/js/checkout.js.map new file mode 100644 index 00000000..52f8e64d --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/checkout.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/checkout.js"],"names":["waitFor","elementFinder","callback","waiter","setInterval","element","clearInterval","hasClass","cls","className","indexOf","resetValidations","form","selector","Array","from","querySelectorAll","forEach","inputRequired","remove","classList","style","display","addRequiredClassToInputs","inputNodeList","validationClass","inputToValidate","add","validationFormListener","creditCardOptions","inputSelector","cardOption","selectSelector","addEventListener","event","parentElement","target","initCreditCardOption","creditCardOption","querySelector","initCreditCardWithoutSavedCards","initCreditCardForm","length","handleEbanxForm","countryCode","paymentType","formListId","document","formList","getById","initCreditCardOptions","responseData","getElementById","cardName","cardExpirationMonth","cardNumber","cardExpirationYear","cardCvv","ebanxToken","ebanxBrand","ebanxMaskedCardNumber","ebanxDeviceFingerprint","ebanxMode","ebanxCountry","mode","value","blurTargetElement","EBANX","config","setMode","ebanxIntegrationKey","setCountry","placeOrderButton","shouldDisable","disabled","saveToken","data","response","errorMessage","error","message","errors","status_code","disableBtnPlaceOrder","Validation","advices","showAdvice","errorDiv","payment_type_code","deviceId","forceClickInPlaceOrder","elem","initEvent","createEvent","dispatchEvent","handleToken","ev","blurTarget","isFormEmpty","relatedTarget","type","card_number","card_name","replace","card_due_date","parseInt","card_cvv","hideAdvice","clearResponseData","elm","setAttribute"],"mappings":"aACA,IAAAA,QAAA,SAAAC,EAAAC,GAGE,IAAMC,EAASC,YAAY,WADvBJ,IAAAA,EAAUC,IACd,GAAME,MAASC,EACb,OAAMC,EAEJC,cAAAH,GACDD,EAAAG,IACDC,MANJC,SAAA,SAAAF,EAAAG,GAYE,OAAO,IAAKH,EAAQI,UAAb,KAA2BC,QAA3B,IAAuCF,EAAvC,MAAkD,GAD3DG,iBAAA,SAAAC,EAAAC,GAKEC,MAAMC,KAAKH,EAAKI,iBAAiBH,IAAWI,QAAQ,SAACC,GADjDP,EAAAA,UAAmBQ,OAAnBR,iBAAmB,oBAAoB,kBACrCI,EAAUC,oBAAhBT,SAAAW,EAAqDA,mBAAkB,uBACrEA,EAAcE,mBAAiBC,MAAAC,QAAkB,WAFrDC,yBAAA,SAAAC,EAAAC,EAAAb,EAAAC,GAUEF,iBAAiBC,EAAMC,GADzBC,MAAMS,KAAAA,GAAAA,QAA2B,SAAAG,GAC/Bf,EAAiBC,UAAMC,IAAvBY,GACMV,SAAKS,EAAeP,wBACxBS,EAAgBN,UAAUO,IAAIF,kBAC9BC,EAAaA,UAATP,OAA0B,sBAJlCS,uBAAA,SAAAhB,EAAAiB,GAYE,IAAMC,EAAgB,wBADlBF,EAAAA,yBACJd,MAAMgB,KAAAA,GAAgBb,QAAA,SAAAc,GACtBA,EAAMC,cAAiB,qBAAvBC,iBAAA,SAAA,SAAAC,GACApB,yBAAAoB,EAA8BjB,OAAQkB,cAACJ,iBAAeD,GAAA,iBAAAlB,EAAAkB,GACpDC,yBAAyBG,EAAAE,OAAAD,cAAqBF,iBAA9CD,GAAyE,kBAAWpB,EAAAoB,QAJxFK,qBAAA,SAAAC,EAAA1B,GAYE,IAAMP,EAAUiC,EAAiBC,cAAc,qBAD3CF,EAAAA,wBACJhC,EAAMA,SAAUiC,EAChBf,yBAAsBlB,EAAA8B,cAAtBnB,iBAAAc,GAAA,iBAAAlB,EAAAkB,IAFFU,gCAAA,SAAA5B,GAQEA,EAAKI,iBAAiB,yBAAyBC,QAAQ,SAACS,GADpDc,EAAAA,UAAAA,IAAAA,oBAEFd,EAAAA,iBAAgBN,0BAAhBH,QAAA,SAAAS,GADFA,EAAAN,UAAAO,IAAA,sBADFc,mBAAA,SAAAZ,EAAAjB,GAUmC,IAA7BiB,EAAkBa,QADlBD,uBAAqB7B,EAAAiB,GACzBQ,qBAAsBK,EAAc,GAAA9B,IAElCyB,gCAAqBR,IAHzBc,gBAAA,SAAAC,EAAAC,EAAAC,GAYIL,QAAAA,WAFF,OAAAM,SAAAR,cAAA,IAAAO,IAA8B,SAACE,GAD7BL,IAAAA,EAAkBK,EAAlBL,iBAAmBC,6BAA2CH,mBAAAZ,EAAAmB,KAO9D,IAAAC,EAAOF,SAASR,GADlB,OAEGW,SAAAA,eAFH7C,IAKE8C,EAAgBC,KAKZC,EAAWJ,EAAQ,SAAWJ,EAAc,IAAMD,EAAc,IAAMC,EAAc,SAFtFM,EAAeF,EAAnB,SAAAJ,EAAA,IAAAD,EAAA,IAAAC,EAAA,WAIMS,EAAsBL,EAAQ,SAAWJ,EAAc,IAAMD,EAAc,eAF3ES,EAAmBJ,EAAWJ,SAAXA,EAA+BD,IAA/BA,EAAmDC,kBACtEU,EAAAA,EAAaN,SAAQJ,EAAA,IAAAD,EAAA,IAAAC,EAAA,QACrBS,EAAAA,EAAsBL,SAAQJ,EAAWA,IAAXD,EAA+BA,UAC7DY,EAAAA,EAAqBP,SAAQJ,EAAWA,IAAcD,EAAMA,UAC5Da,EAAkBR,EAAWJ,SAAcA,EAAMD,IAAcA,EAAMC,uBACrEa,EAAqBT,EAAWJ,SAAcA,EAAMD,IAAcA,EAAxE,uBACMe,EAAaV,EAAQ,SAAWJ,EAAc,IAAMD,EAAc,SAClEgB,EAAwBX,EAAQ,SAAWJ,EAAc,IAAMD,EAAc,oBAC7EiB,EAAAA,EAAyBZ,SAAQJ,EAAWA,IAAXD,EAA+BA,YAChEkB,EAAYb,EAAQ,uBAEpBc,OAAN,IAA6Bd,EAAA,sBAA+BL,EAAc,IAAAA,GAKpEoB,EAA2B,YAApBF,EAAUG,MAAsB,OAAS,aAElDC,EAAoB,KAExBC,MAAMC,OAAOC,QAAQL,GAFrBG,MAAID,OAAAA,kBAAJI,EAAAL,OAIAE,MAAMC,OAAOG,WAAWR,EAAaE,OADrCE,IAYQK,EAAmBzB,SAAAA,GACzB,IAAIyB,EAAOA,SAAqBjC,cAAeiC,2CACjBC,IAA5BD,GAAAA,IACDA,EAAAE,SAAAD,IAaGE,EAAUC,SAAAA,GACZ,IAAAC,EAAcA,KAAAA,eAAd,UAAA,CACA,IAAIC,EAAAA,EAAeC,MAAMC,IAArBF,EAAeC,EAAMC,QAiB1B,OAdGb,EAAMc,UACNH,MAAAA,OAAAA,uBAAeC,EAAwBG,aACxCJ,EAAAX,MAAAc,OAAAD,SAAA,mFAGDG,EAAAA,UAAqBL,EAArBK,GAAqB,GAGnBC,WAAAA,WACEC,WAASC,YADXD,SAAA,GAGCE,EAJH,wBAIG,MAGJ,EAGD7B,EAAWO,EAAQd,KACnBQ,EAAWM,MAAQd,EAAaqC,MAChC5B,EAAAA,MAAAA,EAA8BT,kBAC9BU,EAAAA,MAAAV,EAA+BA,mBAA/BU,EAAuBI,MAAQd,EAAasC,SAG5CC,GAAAA,GArCW,SAAAC,GACT,IAAAA,EACD,OAAA,EAEDzD,IAAAA,EAAM0D,SAAUC,YAAe,SAC/BF,EAAKG,UAAAA,SAAL,GAAA,GANFH,EAAAG,cAAA5D,GASAwD,CAAAxB,IAkDM6B,EAAJ,SAAAC,GAjBmBC,IAAAA,EA3Df7B,EAAOG,MAAWR,QAInBV,EAASY,MAAMvB,QAFdwD,EAAcjC,MAAdiC,QACJ1C,EAAmBS,MAAMvB,QAD3Be,EAAAQ,MAAAvB,WAyDqBuD,EAmBlBD,EAAAG,gBAlBC,WAAAjC,EAAoB+B,MAApB,SAAAA,EAAAG,OACDlC,EAAA+B,GAGCd,IAAAA,GAAqB,GAGnBkB,MAAAA,KAAAA,aACAC,YAAWjD,SAASY,EAFCA,MAAAsC,QAAA,KAAA,KAGrBC,UAAAA,EAAgBC,MAChBC,eAAUjD,SAAQQ,EAAAA,QAAAA,GAAAA,KAAAA,SAAAA,EAAAA,QAAAA,GAJpByC,SAAAjD,EAAAQ,OAMDU,MAUDxB,EAAA,WACAO,EAAWO,KACXN,EAAWM,MAAQ,GACnBL,EAAAA,MAAAA,GACAC,EAAAA,MAAA,GACAuB,EAAsBnB,MAAA,GACpBoB,WAASsB,YADXtB,SAAA,GANFE,IAYElC,IACAE,EAAAA,iBAAWtB,OAAX8D,GAAA,GACAzC,EAAAA,iBAAoBrB,OAApB8D,GAA6CA,GAC7CvC,EAAmBvB,iBAAiB,OAAQ8D,GAAa,GACzDtC,EAAQxB,iBAAyB8D,OAAjCA,GAAA,GAAAtC,EAAQxB,iBAAiB,OAAQ8D,GAAa,GAG9CxC,EAAAA,iBAAWtB,SAAX2E,GAAA,GACAtD,EAAAA,iBAAoBrB,SAAiB2E,GAAUA,GAC/CpD,EAAmBvB,iBAAiB,SAAU2E,GAAmB,GACjEnD,EAAQxB,iBAA2B2E,SAAAA,GAAnC,GACDnD,EAAAxB,iBAAA,SAAA2E,GAAA,IAGCxG,EAAAA,iBAAuB,QAAA,SAAAyG,GACrBpD,YAAQqD,WACRrD,EAAKqD,aAAM,YAAuB,IAChCrD,IAAQqD,EAAAA,OAARrG,UAAA,KAAAC,QAAA,WAAA,GACD+C,EAAAqD,aAAA,YAAA,IAGCrD,IAAQQ,EAAR7B,OAAA3B,UAAA,KAAAC,QAAA,cAAA,IACD+C,EAAAQ,MAAA,KATL","file":"checkout.js","sourcesContent":["/* global EBANX */\n/* global Validation */\n\nconst waitFor = (elementFinder, callback) => {\n const waiter = setInterval(() => {\n const element = elementFinder();\n if (typeof element === 'undefined' || element === null) {\n return false;\n }\n clearInterval(waiter);\n callback(element);\n }, 500);\n};\n\nconst hasClass = (element, cls) => {\n return (` ${element.className} `).indexOf(` ${cls} `) > -1;\n};\n\nconst resetValidations = (form, selector) => {\n Array.from(form.querySelectorAll(selector)).forEach((inputRequired) => {\n inputRequired.classList.remove('required-entry', 'validation-failed', 'brand-required');\n if (inputRequired.nextElementSibling && hasClass(inputRequired.nextElementSibling, 'validation-advice')) {\n inputRequired.nextElementSibling.style.display = 'none';\n }\n });\n};\n\nconst addRequiredClassToInputs = (inputNodeList, validationClass, form, selector) => {\n resetValidations(form, selector);\n Array.from(inputNodeList).forEach((inputToValidate) => {\n inputToValidate.classList.add(validationClass);\n if (hasClass(inputToValidate, 'hidden-input-brand')) {\n inputToValidate.classList.add('brand-required');\n inputToValidate.classList.remove('required-entry');\n }\n });\n};\n\nconst validationFormListener = (form, creditCardOptions) => {\n const inputSelector = '.required-entry-input';\n const selectSelector = '.required-entry-select';\n Array.from(creditCardOptions).forEach((cardOption) => {\n cardOption.querySelector('input[type=radio]').addEventListener('change', (event) => {\n addRequiredClassToInputs(event.target.parentElement.querySelectorAll(inputSelector), 'required-entry', form, inputSelector);\n addRequiredClassToInputs(event.target.parentElement.querySelectorAll(selectSelector), 'validate-select', form, selectSelector);\n });\n });\n};\n\nconst initCreditCardOption = (creditCardOption, form) => {\n const element = creditCardOption.querySelector('input[type=radio]');\n const inputSelector = '.required-entry-input';\n element.checked = true;\n addRequiredClassToInputs(element.parentElement.querySelectorAll(inputSelector), 'required-entry', form, inputSelector);\n};\n\nconst initCreditCardWithoutSavedCards = (form) => {\n form.querySelectorAll('.required-entry-input').forEach((inputToValidate) => {\n inputToValidate.classList.add('required-entry');\n });\n form.querySelectorAll('.required-entry-select').forEach((inputToValidate) => {\n inputToValidate.classList.add('validate-select');\n });\n};\n\nconst initCreditCardForm = (creditCardOptions, form) => {\n if (creditCardOptions.length !== 0) {\n validationFormListener(form, creditCardOptions);\n initCreditCardOption(creditCardOptions[0], form);\n } else {\n initCreditCardWithoutSavedCards(form);\n }\n};\n\nvar handleEbanxForm = (countryCode, paymentType, formListId) => { // eslint-disable-line no-unused-vars\n const initCreditCardOptions = (formList) => {\n const creditCardOptions = formList.querySelectorAll('.ebanx-credit-card-option');\n initCreditCardForm(creditCardOptions, formList);\n };\n\n waitFor(() => {\n return document.querySelector(`#${formListId}`);\n }, initCreditCardOptions);\n\n const getById = function (element) {\n return document.getElementById(element);\n };\n\n let responseData = null;\n\n const cardName = getById('ebanx_' + paymentType + '_' + countryCode + '_' + paymentType + '_name');\n const cardNumber = getById('ebanx_' + paymentType + '_' + countryCode + '_' + paymentType + '_number');\n const cardExpirationMonth = getById('ebanx_' + paymentType + '_' + countryCode + '_expiration');\n const cardExpirationYear = getById('ebanx_' + paymentType + '_' + countryCode + '_expiration_yr');\n const cardCvv = getById('ebanx_' + paymentType + '_' + countryCode + '_' + paymentType + '_cid');\n const ebanxToken = getById('ebanx_' + paymentType + '_' + countryCode + '_token');\n const ebanxBrand = getById('ebanx_' + paymentType + '_' + countryCode + '_brand');\n const ebanxMaskedCardNumber = getById('ebanx_' + paymentType + '_' + countryCode + '_masked_card_number');\n const ebanxDeviceFingerprint = getById('ebanx_' + paymentType + '_' + countryCode + '_device_fingerprint');\n const ebanxMode = getById('ebanx_' + paymentType + '_' + countryCode + '_mode');\n const ebanxIntegrationKey = getById('ebanx_' + paymentType + '_' + countryCode + '_integration_key');\n const ebanxCountry = getById('ebanx_' + paymentType + '_' + countryCode + '_country');\n const errorDiv = getById('ebanx-error-message');\n\n const hasEbanxForm = typeof getById('payment_form_ebanx_' + paymentType + '_' + countryCode) !== 'undefined';\n\n const mode = ebanxMode.value === 'sandbox' ? 'test' : 'production';\n\n let blurTargetElement = null;\n\n EBANX.config.setMode(mode);\n EBANX.config.setPublishableKey(ebanxIntegrationKey.value);\n EBANX.config.setCountry(ebanxCountry.value);\n\n const isFormEmpty = () => {\n return !cardNumber.value.length ||\n !cardName.value.length ||\n !cardExpirationMonth.value.length ||\n !cardExpirationYear.value.length ||\n !cardCvv.value.length;\n };\n\n const disableBtnPlaceOrder = (shouldDisable) => {\n const placeOrderButton = document.querySelector('#review-buttons-container > button');\n if (typeof placeOrderButton !== 'undefined' && placeOrderButton) {\n placeOrderButton.disabled = shouldDisable;\n }\n };\n\n const forceClickInPlaceOrder = (elem) => {\n if (!elem) {\n return false;\n }\n var event = document.createEvent('Event');\n event.initEvent('click', true, true);\n elem.dispatchEvent(event);\n };\n\n const saveToken = (response) => {\n if (!response.data.hasOwnProperty('status')) {\n const error = response.error.err;\n let errorMessage = error.message;\n\n if (!error.message) {\n EBANX.errors.InvalidValueFieldError(error.status_code);\n errorMessage = EBANX.errors.message || 'Some error happened. Please, verify the data of your credit card and try again.';\n }\n\n errorDiv.innerHTML = errorMessage;\n disableBtnPlaceOrder(false);\n\n setTimeout(() => {\n Validation.showAdvice({\n advices: false,\n }, errorDiv, 'ebanx-error-message');\n }, 500);\n\n return false;\n }\n\n responseData = response.data;\n ebanxToken.value = responseData.token;\n ebanxBrand.value = responseData.payment_type_code;\n ebanxMaskedCardNumber.value = responseData.masked_card_number;\n ebanxDeviceFingerprint.value = responseData.deviceId;\n\n disableBtnPlaceOrder(false);\n forceClickInPlaceOrder(blurTargetElement);\n };\n\n const generateToken = (blurTarget) => {\n if (blurTarget && (blurTarget.type === 'button' || blurTarget.type === 'span')) {\n blurTargetElement = blurTarget;\n }\n\n if (!responseData) {\n disableBtnPlaceOrder(true);\n\n EBANX.card.createToken({\n card_number: parseInt(cardNumber.value.replace(/ /g, '')),\n card_name: cardName.value,\n card_due_date: (parseInt(cardExpirationMonth.value) || 0) + '/' + (parseInt(cardExpirationYear.value) || 0),\n card_cvv: cardCvv.value,\n }, saveToken);\n }\n };\n\n const handleToken = (ev) => {\n if (!isFormEmpty()) {\n generateToken(ev.relatedTarget);\n }\n };\n\n const clearResponseData = () => {\n responseData = null;\n ebanxToken.value = '';\n ebanxBrand.value = '';\n ebanxMaskedCardNumber.value = '';\n ebanxDeviceFingerprint.value = '';\n Validation.hideAdvice({\n advices: false,\n }, errorDiv);\n };\n\n if (hasEbanxForm) {\n cardName.addEventListener('blur', handleToken, false);\n cardNumber.addEventListener('blur', handleToken, false);\n cardExpirationMonth.addEventListener('blur', handleToken, false);\n cardExpirationYear.addEventListener('blur', handleToken, false);\n cardCvv.addEventListener('blur', handleToken, false);\n\n cardName.addEventListener('change', clearResponseData, false);\n cardNumber.addEventListener('change', clearResponseData, false);\n cardExpirationMonth.addEventListener('change', clearResponseData, false);\n cardExpirationYear.addEventListener('change', clearResponseData, false);\n cardCvv.addEventListener('change', clearResponseData, false);\n }\n\n cardNumber.addEventListener('input', function (elm) {\n setInterval(function() {\n cardCvv.setAttribute('maxlength', 3);\n if ((' ' + elm.target.className + ' ').indexOf(' amex ') > -1) {\n cardCvv.setAttribute('maxlength', 4);\n }\n\n if ((' ' + elm.target.className + ' ').indexOf(' unknown ') > -1) {\n cardCvv.value = '';\n }\n }, 200);\n });\n};\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/document.js b/skin/frontend/base/default/ebanx/js/document.js new file mode 100644 index 00000000..7eb741aa --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/document.js @@ -0,0 +1,2 @@ +"use strict";var defaultLabel=null,taxVatLabel=null,taxVatInput=null,countrySelect=null,getLabelByCountry=function(e,t){switch(e.toLowerCase()){case"br":return EBANXData.brazilAllowedDocumentFields.join(" / ").toUpperCase();case"co":return"DNI";case"cl":return"RUT";case"ar":return"Document";default:return t}},OSCRequire=function(e){"undefined"!=typeof ebanxRemoveOSCRequireDocument&&ebanxRemoveOSCRequireDocument(e)},changeTaxVatLabel=function(){var e=countrySelect.value,t=getLabelByCountry(e,defaultLabel);OSCRequire(e),setTimeout(function(){taxVatInput.placeholder=""},10),taxVatLabel.innerHTML=t,"BR"!==e&&"CO"!==e&&"CL"!==e&&"AR"!==e||setTimeout(function(){taxVatInput.placeholder=t},10),inputHandler(taxVatInput,e)},removeDocumentTypeField=function(){var e=document.getElementById("ebanx-document-type");"AR"!==countrySelect.value&&e&&null!==e&&e.remove()},addDocumentTypeField=function(){if("AR"===countrySelect.value&&!document.getElementById("billing:ebanx_document_type")){var e=document.createElement("li");e.className="fields",e.innerHTML='
    \n \n
    \n \n
    \n
    ',taxVatInput.closest("ul").insertBefore(e,taxVatInput.closest("li"))}},init=function(){taxVatLabel=document.querySelector('label[for="billing\\:taxvat"]'),taxVatInput=document.getElementById("billing:taxvat"),countrySelect=document.querySelector("#billing\\:country_id"),(EBANXData.maskTaxVat||taxVatLabel||taxVatInput||null!==taxVatInput)&&(addDocumentTypeField(),taxVatLabel||"function"!=typeof amsCheckoutHandler||(taxVatLabel=amsCheckoutHandler()),countrySelect&&(defaultLabel=taxVatLabel.innerHTML,countrySelect.addEventListener("change",changeTaxVatLabel),countrySelect.addEventListener("change",addDocumentTypeField),countrySelect.addEventListener("change",removeDocumentTypeField),countrySelect.dispatchEvent(new Event("change"))))};document.addEventListener("DOMContentLoaded",init); +//# sourceMappingURL=document.js.map diff --git a/skin/frontend/base/default/ebanx/js/document.js.map b/skin/frontend/base/default/ebanx/js/document.js.map new file mode 100644 index 00000000..23586930 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/document.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/document.js"],"names":["defaultLabel","taxVatLabel","countrySelect","country","toLowerCase","getLabelByCountry","join","toUpperCase","EBANXData","OSCRequire","ebanxRemoveOSCRequireDocument","changeTaxVatLabel","value","newLabel","setTimeout","placeholder","taxVatInput","inputHandler","$documentTypeSelect","document","removeDocumentTypeField","getElementById","addDocumentTypeField","createElement","div","ul","insertBefore","closest","className","innerHTML","amsCheckoutHandler","querySelector","addEventListener","dispatchEvent","Event","init"],"mappings":"aAGA,IAAAA,aAAA,KAGIC,YAAc,KADdD,YAAAA,KACAC,cAAc,KAEdC,kBAAJ,SAAAC,EAAAH,GAGE,OAAQG,EAAQC,eADZC,IAAAA,KACJ,OAAQF,UAAQC,4BAAhBE,KAAA,OAAAC,cACE,IAAK,KACH,MAAOC,MACT,IAAK,KACH,MAAO,MACT,IAAK,KACH,MAAO,WACT,QACE,OAAOR,IATbS,WAAA,SAAAN,GAgB+C,oBAAlCO,+BADPD,8BAAAA,IAANE,kBAAA,WAOE,IAAMR,EAAUD,cAAcU,MAD1BD,EAAAA,kBAAAA,EAAAA,cAEJF,WAAMI,GAENJ,WAGE,WADFK,YACEC,YAAW,IADb,IAQAd,YAAIE,UAAoBA,EAEpB,OADFW,GACQ,OAANX,GAAM,OAAAA,GAAA,OAAAA,GACJa,WAFJ,WAMDA,YAAAD,YAAAF,GAEDI,IAIAA,aAAMC,YAAsBC,IAG3BC,wBAAA,WAHD,IAAMF,EAAsBC,SAASE,eAAe,uBAKpD,OAAAH,cAAAA,OAAAA,GAAA,OAAAA,GAIAA,EAAkBN,UADdU,qBAAuB,WAK3B,GAAqBC,OAAfC,cAAML,QAAuBA,SAAnCE,eAAA,+BAAA,CAgBAI,IAAGC,EAAAA,SAAkBV,cAAYW,MArBnCH,EAAAI,UAAA,SAOEJ,EAAIK,UAAJ,slBA6BE5B,YAAc6B,QAAAA,MACfJ,aAAAF,EAAAR,YAAAW,QAAA,SAGC3B,KAAAA,WAfFC,YAAakB,SAASY,cAAc,iCAiBlC7B,YAAAA,SAAc8B,eAAiB,kBAC/B9B,cAAc8B,SAAAA,cAAiB,0BAE/B9B,UAAAA,YAA4BD,aAAUe,aAAtC,OAAAA,eAIJG,uBAdOlB,aAA6C,mBAAvB6B,qBACzB7B,YAAc6B,sBAGZ5B,gBACFF,aAAeC,YAAY4B,UAE3B3B,cAAc8B,iBAAiB,SAAUrB,mBACzCT,cAAc8B,iBAAiB,SAAUV,sBACzCpB,cAAc8B,iBAAiB,SAAUZ,yBACzClB,cAAc+B,cAAc,IAAIC,MAAM,cAI1Cf,SAASa,iBAAiB,mBAAoBG","file":"document.js","sourcesContent":["/* global EBANXData */\n/* global amsCheckoutHandler */\n/* global ebanxRemoveOSCRequireDocument */\n/* global inputHandler */\n\nlet defaultLabel = null;\nlet taxVatLabel = null;\nlet taxVatInput = null;\nlet countrySelect = null;\n\nconst getLabelByCountry = (country, defaultLabel) => {\n switch (country.toLowerCase()) {\n case 'br':\n return EBANXData.brazilAllowedDocumentFields.join(' / ').toUpperCase();\n case 'co':\n return 'DNI';\n case 'cl':\n return 'RUT';\n case 'ar':\n return 'Document';\n default:\n return defaultLabel;\n }\n};\n\nconst OSCRequire = country => {\n if (typeof ebanxRemoveOSCRequireDocument !== 'undefined') {\n ebanxRemoveOSCRequireDocument(country);\n }\n};\n\nconst changeTaxVatLabel = () => {\n const country = countrySelect.value;\n const newLabel = getLabelByCountry(country, defaultLabel);\n\n OSCRequire(country);\n\n setTimeout(\n function() {\n taxVatInput.placeholder = '';\n },\n 10\n );\n\n taxVatLabel.innerHTML = newLabel;\n if (country === 'BR' || country === 'CO' || country === 'CL' || country === 'AR') {\n setTimeout(\n () => {\n taxVatInput.placeholder = newLabel;\n },\n 10\n );\n }\n\n inputHandler(taxVatInput, country);\n};\n\nconst removeDocumentTypeField = () => {\n const $documentTypeSelect = document.getElementById('ebanx-document-type');\n if (countrySelect.value === 'AR' || !$documentTypeSelect || $documentTypeSelect === null) {\n return;\n }\n\n $documentTypeSelect.remove();\n};\n\nconst addDocumentTypeField = () => {\n if (countrySelect.value !== 'AR' || document.getElementById('billing:ebanx_document_type')) {\n return;\n }\n\n const div = document.createElement('li');\n div.className = 'fields';\n div.innerHTML = `
    \n \n
    \n \n
    \n
    `;\n const ul = taxVatInput.closest('ul');\n ul.insertBefore(div, taxVatInput.closest('li'));\n};\n\nconst init = () => {\n taxVatLabel= document.querySelector('label[for=\"billing\\\\:taxvat\"]');\n taxVatInput = document.getElementById('billing:taxvat');\n countrySelect = document.querySelector('#billing\\\\:country_id');\n\n if (!EBANXData.maskTaxVat && !taxVatLabel && !taxVatInput && taxVatInput === null) {\n return;\n }\n\n addDocumentTypeField();\n\n if (!taxVatLabel && typeof amsCheckoutHandler === 'function') {\n taxVatLabel = amsCheckoutHandler();\n }\n\n if (countrySelect) {\n defaultLabel = taxVatLabel.innerHTML;\n\n countrySelect.addEventListener('change', changeTaxVatLabel);\n countrySelect.addEventListener('change', addDocumentTypeField);\n countrySelect.addEventListener('change', removeDocumentTypeField);\n countrySelect.dispatchEvent(new Event('change'));\n }\n};\n\ndocument.addEventListener('DOMContentLoaded', init);\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/firecheckout-interest.js b/skin/frontend/base/default/ebanx/js/firecheckout-interest.js new file mode 100644 index 00000000..ac2c38aa --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/firecheckout-interest.js @@ -0,0 +1,2 @@ +"use strict";var ebanxUpdateFireInterest=function(){checkout.update(checkout.urls.payment_method)}; +//# sourceMappingURL=firecheckout-interest.js.map diff --git a/skin/frontend/base/default/ebanx/js/firecheckout-interest.js.map b/skin/frontend/base/default/ebanx/js/firecheckout-interest.js.map new file mode 100644 index 00000000..1355c706 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/firecheckout-interest.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/firecheckout-interest.js"],"names":["ebanxUpdateFireInterest","checkout","update","urls","payment_method"],"mappings":"aAAA,IAAAA,wBAAA,WAGEC,SAASC,OAAOD,SAASE,KAAKC","file":"firecheckout-interest.js","sourcesContent":["/* global checkout */\n\nvar ebanxUpdateFireInterest = () => {// eslint-disable-line no-unused-vars\n checkout.update(checkout.urls.payment_method);\n};\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/input-handler.js b/skin/frontend/base/default/ebanx/js/input-handler.js new file mode 100644 index 00000000..8648bf94 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/input-handler.js @@ -0,0 +1,2 @@ +"use strict";var maskValues={br:{masks:["999.999.999-999","99.999.999/9999-99"],changeOnLenght:14},ar:{masks:["SS-SSSSSSSS-S"],changeOnLenght:0},co:{masks:["999999999999999999"],changeOnLenght:0},cl:{masks:["99.999.999-S"],changeOnLenght:0}},handler=function(e,a,t){var n=0;0!==a&&(n=t.value.length>a?1:0),VMasker(t).unMask(),VMasker(t).maskPattern(e[n]),t.value=VMasker.toPattern(t.value,e[n])},inputHandler=function(e,a){if(maskValues[a.toLowerCase()]&&e){var t=maskValues[a.toLowerCase()];VMasker(e).maskPattern(t.masks[0]),e.addEventListener("input",function(e){handler(t.masks,t.changeOnLenght,e.target)},!1)}},initTaxVatLabel=function(e){var a=document.querySelector('label[for="taxvat"]');a&&(a.innerHTML=e)},hideTaxVat=function(){var e=document.querySelector('label[for="taxvat"]');e&&(e.style.display="none");var a=document.getElementById("taxvat");a&&(a.style.display="none")},selectOption=function(e,a){e&&null!==e&&e.forEach(function(e){e.value===a&&e.setAttribute("selected","selected")})}; +//# sourceMappingURL=input-handler.js.map diff --git a/skin/frontend/base/default/ebanx/js/input-handler.js.map b/skin/frontend/base/default/ebanx/js/input-handler.js.map new file mode 100644 index 00000000..e9473c9e --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/input-handler.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/input-handler.js"],"names":["maskValues","br","changeOnLenght","masks","ar","co","cl","handler","max","element","toggleMask","value","length","VMasker","maskPattern","toPattern","inputHandler","inputDoc","country","toLowerCase","docMaskValues","e","target","taxvatLabel","document","querySelector","innerHTML","label","style","display","hideTaxVat","taxvat","selectOption","elements","option","getElementById","forEach","elem"],"mappings":"aAAA,IAAAA,YAGEC,IADID,OAAAA,kBAAa,sBACjBC,eAAI,IAEFC,IAHeC,OAAA,iBAKjBC,eAAI,GAEFF,IAPeC,OAAA,sBASjBE,eAAI,GAEFH,IAXeC,OAAA,gBAajBG,eAAI,IAbNC,QAAA,SAAAJ,EAAAK,EAAAC,GAoBE,IAAIC,EAAa,EADbH,IAAAA,IACJG,EAAIA,EAAJC,MAAAC,OAAAJ,EAAA,EAAA,GAEEE,QAAAA,GAAaD,SACdI,QAAAJ,GAAAK,YAAAX,EAAAO,IACDG,EAAQJ,MAARI,QAAAE,UAAAN,EAAAE,MAAAR,EAAAO,KALFM,aAAA,SAAAC,EAAAC,GAWE,GAAIlB,WAAWkB,EAAQC,gBAAkBF,EAAU,CAD/CD,IAAAA,EAAehB,WAAfgB,EAAgBC,eAAwBJ,QAAAI,GAAAH,YAAAM,EAAAjB,MAAA,IAC5Cc,EAAIjB,iBAAmBmB,QAAnB,SAAAE,GAAqCJ,QAAUG,EAAAjB,MAAAiB,EAAAlB,eAAAmB,EAAAC,UAAA,KAGLf,gBAAQa,SAAAA,GAA+D,IAAEG,EAArHC,SAAAC,cAAA,uBACDF,IALHA,EAAAG,UAAAC,IASQJ,WAAAA,WACN,IAAIA,EAAaC,SAAAC,cAAA,uBACfF,IACDA,EAAAK,MAAAC,QAAA,QAGH,IAAMC,EAAaN,SAAbM,eAAmB,UAAEC,IACzBA,EAAMR,MAAAA,QAAcC,SAWhBQ,aAAe,SAACC,EAAUC,GANxBH,GAAkBI,OAATX,GANjBS,EAAAG,QAAA,SAAAC,GAkBOA,EAAK1B,QAAUuB,GANhBF,EAAAA,aAAe,WAAfA","file":"input-handler.js","sourcesContent":["/* global VMasker */\n\nconst maskValues = {\n br: {\n masks: ['999.999.999-999', '99.999.999/9999-99'],\n changeOnLenght: 14,\n },\n ar: {\n masks: ['SS-SSSSSSSS-S'],\n changeOnLenght: 0,\n },\n co: {\n masks: ['999999999999999999'],\n changeOnLenght: 0,\n },\n cl: {\n masks: ['99.999.999-S'],\n changeOnLenght: 0,\n },\n};\n\nconst handler = (masks, max, element) => {\n let toggleMask = 0;\n if(max !== 0){\n toggleMask = element.value.length > max ? 1 : 0;\n }\n VMasker(element).unMask();\n VMasker(element).maskPattern(masks[toggleMask]);\n element.value = VMasker.toPattern(element.value, masks[toggleMask]);\n};\n\nconst inputHandler = (inputDoc, country) => { // eslint-disable-line no-unused-vars\n if (maskValues[country.toLowerCase()] && inputDoc) {\n const docMaskValues = maskValues[country.toLowerCase()];\n VMasker(inputDoc).maskPattern(docMaskValues.masks[0]);\n inputDoc.addEventListener('input', (e) => { handler(docMaskValues.masks, docMaskValues.changeOnLenght, e.target); }, false);\n }\n};\n\nconst initTaxVatLabel = (label) => { // eslint-disable-line no-unused-vars\n const taxvatLabel = document.querySelector('label[for=\"taxvat\"]');\n if (taxvatLabel) {\n taxvatLabel.innerHTML = label;\n }\n};\n\nconst hideTaxVat = () => { // eslint-disable-line no-unused-vars\n const taxvatLabel = document.querySelector('label[for=\"taxvat\"]');\n if (taxvatLabel) {\n taxvatLabel.style.display = 'none';\n }\n\n const taxvat = document.getElementById('taxvat');\n if (taxvat) {\n taxvat.style.display = 'none';\n }\n};\n\nconst selectOption = (elements, option) => { // eslint-disable-line no-unused-vars\n if (!elements || elements === null) {\n return;\n }\n\n elements.forEach((elem) => {\n if(elem.value === option) {\n elem.setAttribute('selected', 'selected');\n }\n });\n};\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/instalments.js b/skin/frontend/base/default/ebanx/js/instalments.js new file mode 100644 index 00000000..78df4632 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/instalments.js @@ -0,0 +1,2 @@ +"use strict";var initInstalments=function(e,t){var n=document.querySelector("#"+e+"_instalments");"undefined"!=typeof ebanxUpdateFireInterest&&n&&n.addEventListener("change",function(){ebanxUpdateFireInterest()});var a=function(){var e=document.querySelector("#cc-"+t+"-local-amount"),a=n.options[n.selectedIndex],r=!(!a||!a.getAttribute)&&a.getAttribute("data-local-amount");e&&e.innerHTML&&r&&(e.innerHTML=" "+r+" ")};n&&(document.addEventListener("DOMContentLoaded",a),n.addEventListener("change",a))}; +//# sourceMappingURL=instalments.js.map diff --git a/skin/frontend/base/default/ebanx/js/instalments.js.map b/skin/frontend/base/default/ebanx/js/instalments.js.map new file mode 100644 index 00000000..f1715fa4 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/instalments.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/instalments.js"],"names":["initInstalments","code","lowerCountry","selectInstalment","document","querySelector","ebanxUpdateFireInterest","updateInstalment","text","instalmentOption","options","selectedIndex","getAttribute","localAmount","addEventListener"],"mappings":"aAAA,IAAAA,gBAAA,SAAAC,EAAAC,GAGE,IAAMC,EAAmBC,SAASC,cAAT,IAA2BJ,EAA3B,gBADuB,oBAAAK,yBAAAH,GAChDA,EAAMA,iBAA4BE,SAAT,WAIrBC,4BACD,IAFDC,EAAA,WAGD,IAAAC,EAAAJ,SAAAC,cAAA,OAAAH,EAAA,iBAIOO,EAAmBN,EAAiBO,QAAQP,EAAiBQ,eAF/DJ,KAAAA,IAAAA,EAAyBK,eAAAH,EAAAG,aAAA,qBAEvBH,GAAAA,EAAAA,WAAmBN,IACzBK,EAAMK,UAAN,YAAoBJ,EAApB,eAICN,IAPHC,SAAAU,iBAAA,mBAAAP,GAYEJ,EAAiBW,iBAAiB,SAAUP","file":"instalments.js","sourcesContent":["/* global ebanxUpdateFireInterest */\n\nconst initInstalments = (code, lowerCountry) => { // eslint-disable-line no-unused-vars\n const selectInstalment = document.querySelector(`#${code}_instalments`);\n\n if (typeof ebanxUpdateFireInterest !== 'undefined' && selectInstalment) {\n selectInstalment.addEventListener('change', () => {\n ebanxUpdateFireInterest();\n });\n }\n\n const updateInstalment = () => {\n const text = document.querySelector(`#cc-${lowerCountry}-local-amount`);\n const instalmentOption = selectInstalment.options[selectInstalment.selectedIndex];\n const localAmount = instalmentOption && instalmentOption.getAttribute ? instalmentOption.getAttribute('data-local-amount') : false;\n\n if (text && text.innerHTML && localAmount) {\n text.innerHTML = ` ${localAmount} `;\n }\n };\n\n if (selectInstalment) {\n document.addEventListener('DOMContentLoaded', updateInstalment);\n selectInstalment.addEventListener('change', updateInstalment);\n }\n};\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/oneclick.js b/skin/frontend/base/default/ebanx/js/oneclick.js new file mode 100644 index 00000000..5faa96f1 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/oneclick.js @@ -0,0 +1,2 @@ +"use strict";document.addEventListener("DOMContentLoaded",function(){var e=document.getElementById("product-oneclick-ebanx-button"),t=document.getElementById("ebanx-one-click-tooltip"),n=document.getElementById("ebanx-one-click-close-button"),c=document.getElementsByName("payment[selected_card]"),o=document.getElementById("ebanx-oneclick-pay-button"),d=document.getElementById("product_addtocart_form");c.length&&(c[0].checked=!0),e&&e.addEventListener("click",function(e){e.preventDefault(),t.classList.toggle("is-active")}),n&&n.addEventListener("click",function(e){e.preventDefault(),t.classList.remove("is-active")}),o&&d&&o.addEventListener("click",function(){d.action="/ebanx/oneclick/pay",productAddToCartForm.submit(o)})}); +//# sourceMappingURL=oneclick.js.map diff --git a/skin/frontend/base/default/ebanx/js/oneclick.js.map b/skin/frontend/base/default/ebanx/js/oneclick.js.map new file mode 100644 index 00000000..b5978848 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/oneclick.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/oneclick.js"],"names":["document","addEventListener","button","getElementById","tooltip","close","elements","getElementsByName","form","checked","e","payButton"],"mappings":"aAAAA,SAAAC,iBAAA,mBAAA,WAGE,IAAMC,EAASF,SAASG,eAAe,iCADzCH,EAASC,SAAiBE,eAAA,2BAClBD,EAASF,SAASG,eAAe,gCACjCC,EAAUJ,SAASG,kBAAe,0BAClCE,EAAQL,SAASG,eAAe,6BAChCG,EAAAA,SAAWN,eAASO,0BAEpBC,EAAOR,SAGXM,EAAS,GAAGG,SAAU,GACvBP,GAGCA,EAAOD,iBAAiB,QAAS,SAAAS,GAD/BR,EAAAA,iBACFA,EAAOD,UAAAA,OAAiB,eAIzBI,GAGCA,EAAMJ,iBAAiB,QAAS,SAAAS,GAD9BL,EAAAA,iBACFA,EAAMJ,UAAAA,OAAiB,eAIxBU,GAAAH,GAGCG,EAAUV,iBAAiB,QAAS,WADlCU,EAAAA,OAAaH,sBACfG,qBAAUV,OAAiBU","file":"oneclick.js","sourcesContent":["/* global productAddToCartForm */\n\ndocument.addEventListener('DOMContentLoaded', () => {\n const button = document.getElementById('product-oneclick-ebanx-button');\n const tooltip = document.getElementById('ebanx-one-click-tooltip');\n const close = document.getElementById('ebanx-one-click-close-button');\n const elements = document.getElementsByName('payment[selected_card]');\n const payButton = document.getElementById('ebanx-oneclick-pay-button');\n const form = document.getElementById('product_addtocart_form');\n\n if (elements.length) {\n elements[0].checked = true;\n }\n\n if (button) {\n button.addEventListener('click', e => {\n e.preventDefault();\n tooltip.classList.toggle('is-active');\n });\n }\n\n if (close) {\n close.addEventListener('click', e => {\n e.preventDefault();\n tooltip.classList.remove('is-active');\n });\n }\n\n if (payButton && form) {\n payButton.addEventListener('click', () => {\n form.action = '/ebanx/oneclick/pay';\n productAddToCartForm.submit(payButton);\n });\n }\n});\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/onestepcheckout.js b/skin/frontend/base/default/ebanx/js/onestepcheckout.js new file mode 100644 index 00000000..583766e6 --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/onestepcheckout.js @@ -0,0 +1,2 @@ +"use strict";var ebanxRemoveOSCRequireDocument=function(a){var t=document.getElementById("billing:taxvat"),e=document.getElementById("advice-validate-taxvat-billing:taxvat");t.classList.contains("validate-taxvat")||t.classList.add("validate-taxvat"),"BR"!==a&&"CO"!==a&&"CL"!==a&&(t.classList.remove("validate-taxvat"),t.classList.remove("validation-failed"),e&&e.remove())}; +//# sourceMappingURL=onestepcheckout.js.map diff --git a/skin/frontend/base/default/ebanx/js/onestepcheckout.js.map b/skin/frontend/base/default/ebanx/js/onestepcheckout.js.map new file mode 100644 index 00000000..b0fcfd0c --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/onestepcheckout.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/onestepcheckout.js"],"names":["ebanxRemoveOSCRequireDocument","country","doc","document","getElementById","classList","contains","remove","advice"],"mappings":"aAAA,IAAIA,8BAAgC,SAAAC,GAClC,IAAMC,EAAMC,SAASC,eAAe,kBADlCJ,EAAAA,SAAAA,eAA6C,yCAM1CE,EAAIG,UAAUC,SAAS,oBAA5BJ,EAAKA,UAAIG,IAAUC,mBAElB,OAAAL,GAAA,OAAAA,GAAA,OAAAA,IACDC,EAAMD,UAAAA,OAAA,mBACJC,EAAIG,UAAUE,OAAO,qBACjBF,GACJG,EAAIA","file":"onestepcheckout.js","sourcesContent":["var ebanxRemoveOSCRequireDocument = country => { // eslint-disable-line no-unused-vars\n const doc = document.getElementById('billing:taxvat');\n const advice = document.getElementById(\n 'advice-validate-taxvat-billing:taxvat'\n );\n\n if (!doc.classList.contains('validate-taxvat')) {\n doc.classList.add('validate-taxvat');\n }\n if (!(country === 'BR' || country === 'CO' || country === 'CL')) {\n doc.classList.remove('validate-taxvat');\n doc.classList.remove('validation-failed');\n if (advice) {\n advice.remove();\n }\n }\n};\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/success.js b/skin/frontend/base/default/ebanx/js/success.js new file mode 100644 index 00000000..26dc5c4b --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/success.js @@ -0,0 +1,2 @@ +"use strict";document.addEventListener("DOMContentLoaded",function(){var e=new Clipboard(".ebanx-button--copy"),n=document.querySelector(".ebanx-cash-payment iframe");e.on("success",function(e){var n=e.trigger.innerText;e.trigger.innerText="Copiado!",setTimeout(function(){e.trigger.innerText=n},2e3)});var t=function(e){e.style.height=e.contentWindow.document.body.parentElement.scrollHeight+" px"};n&&(window.addEventListener("load",function(){t(n)}),n.contentWindow.addEventListener("resize",function(){t(n)}))}); +//# sourceMappingURL=success.js.map diff --git a/skin/frontend/base/default/ebanx/js/success.js.map b/skin/frontend/base/default/ebanx/js/success.js.map new file mode 100644 index 00000000..86c237df --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/success.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/success.js"],"names":["document","addEventListener","clipboard","Clipboard","querySelector","iframe","text","e","trigger","innerText","setTimeout","style","height","contentWindow","body","parentElement","scrollHeight","window","resizeIframe"],"mappings":"aAAAA,SAAAC,iBAAA,mBAAA,WAGE,IAAMC,EAAY,IAAIC,UAAU,uBADlCH,EAASC,SAAiBG,cAAA,8BAExBF,EAAMG,GAASL,UAASI,SAAAA,GAGtB,IAAME,EAAOC,EAAEC,QAAQC,UAAvBF,EAAAC,QAAMF,UAAiBG,WAEvBF,WAGE,WADFG,EAAAA,QACED,UAAMH,GADR,OASAD,IAAAA,EAAA,SAAAA,GADFA,EAAAM,MAAAC,OAAAP,EAAAQ,cAAAb,SAAAc,KAAAC,cAAAC,aAAA,OAKEC,IACEC,OAAAA,iBAAA,OAAA,WADFA,EAAAb,KAKEa,EAAAA,cAAAjB,iBAAA,SAAA,WADFiB,EAAAb","file":"success.js","sourcesContent":["/* global Clipboard */\n\ndocument.addEventListener('DOMContentLoaded', () => {\n const clipboard = new Clipboard('.ebanx-button--copy');\n const iframe = document.querySelector('.ebanx-cash-payment iframe');\n\n clipboard.on('success', e => {\n const text = e.trigger.innerText;\n\n e.trigger.innerText = 'Copiado!';\n\n setTimeout(\n () => {\n e.trigger.innerText = text;\n },\n 2000\n );\n });\n\n const resizeIframe = iframe => {\n iframe.style.height = `${iframe.contentWindow.document.body.parentElement.scrollHeight} px`;\n };\n\n if (iframe) {\n window.addEventListener('load', () => {\n resizeIframe(iframe);\n });\n\n iframe.contentWindow.addEventListener('resize', () => {\n resizeIframe(iframe);\n });\n }\n});\n"]} \ No newline at end of file diff --git a/skin/frontend/base/default/ebanx/js/validator.js b/skin/frontend/base/default/ebanx/js/validator.js new file mode 100644 index 00000000..79e51bfa --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/validator.js @@ -0,0 +1,2 @@ +"use strict";Validation.add("brand-required"," ",function(t){return!Validation.get("IsEmpty").test(t)}); +//# sourceMappingURL=validator.js.map diff --git a/skin/frontend/base/default/ebanx/js/validator.js.map b/skin/frontend/base/default/ebanx/js/validator.js.map new file mode 100644 index 00000000..405e7d0c --- /dev/null +++ b/skin/frontend/base/default/ebanx/js/validator.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["frontend/base/default/ebanx/js/validator.js"],"names":["Validation","add","v","get","test"],"mappings":"aAAAA,WAAAC,IAAA,iBAAA,IAAA,SAAAC,GAGE,OAAQF,WAAWG,IAAI,WAAWC,KAAKF","file":"validator.js","sourcesContent":["/* global Validation */\n\nValidation.add('brand-required', ' ', (v) => {\n return !Validation.get('IsEmpty').test(v);\n});\n"]} \ No newline at end of file diff --git a/skin/frontend/tm_themes/theme759/images/banner-1.jpg b/skin/frontend/tm_themes/theme759/images/banner-1.jpg index e7c5617c..fac9ffb1 100644 Binary files a/skin/frontend/tm_themes/theme759/images/banner-1.jpg and b/skin/frontend/tm_themes/theme759/images/banner-1.jpg differ diff --git a/skin/frontend/tm_themes/theme759/images/banner-2.jpg b/skin/frontend/tm_themes/theme759/images/banner-2.jpg index 8dedfca7..c4fc714d 100644 Binary files a/skin/frontend/tm_themes/theme759/images/banner-2.jpg and b/skin/frontend/tm_themes/theme759/images/banner-2.jpg differ diff --git a/skin/frontend/tm_themes/theme759/images/banner-3.jpg b/skin/frontend/tm_themes/theme759/images/banner-3.jpg index 30943f58..739c023d 100644 Binary files a/skin/frontend/tm_themes/theme759/images/banner-3.jpg and b/skin/frontend/tm_themes/theme759/images/banner-3.jpg differ diff --git a/skin/frontend/tm_themes/theme759/images/banner-4.jpg b/skin/frontend/tm_themes/theme759/images/banner-4.jpg index a826ff44..beea02eb 100644 Binary files a/skin/frontend/tm_themes/theme759/images/banner-4.jpg and b/skin/frontend/tm_themes/theme759/images/banner-4.jpg differ diff --git a/skin/frontend/tm_themes/theme759/images/slide-1.jpg b/skin/frontend/tm_themes/theme759/images/slide-1.jpg index 35b00af9..c220dd05 100644 Binary files a/skin/frontend/tm_themes/theme759/images/slide-1.jpg and b/skin/frontend/tm_themes/theme759/images/slide-1.jpg differ diff --git a/skin/frontend/tm_themes/theme759/images/slide-2.jpg b/skin/frontend/tm_themes/theme759/images/slide-2.jpg index 888adb2f..c220dd05 100644 Binary files a/skin/frontend/tm_themes/theme759/images/slide-2.jpg and b/skin/frontend/tm_themes/theme759/images/slide-2.jpg differ