diff --git a/composer.json b/composer.json index da4259a..d6ff23f 100644 --- a/composer.json +++ b/composer.json @@ -26,9 +26,10 @@ "type": "library", "minimum-stability": "stable", "require": { - "php": ">=5.4.0", + "php": ">=7.1.0", "ext-soap": "*", - "piotrooo/wsdl-creator": "1.*" + "ext-dom": "*", + "piotrooo/wsdl-creator": "2.0.*" }, "autoload": { "psr-4": { @@ -37,6 +38,7 @@ }, "require-dev": { "phpspec/phpspec": "^2.2", + "phpunit/phpunit": "4.*", "codeception/codeception": "2.0.*" } } diff --git a/src/SamanUssd.php b/src/SamanUssd.php index 3015bd2..701a222 100644 --- a/src/SamanUssd.php +++ b/src/SamanUssd.php @@ -5,25 +5,52 @@ use Nikapps\SamanUssd\Contracts\SamanUssdListener; use Nikapps\SamanUssd\Soap\SamanSoapServer; use SoapServer; -use WSDL\WSDLCreator; +use WSDL\Annotation\BindingType; +use WSDL\Annotation\SoapBinding; +use WSDL\Builder\AnnotationWSDLBuilder; +use WSDL\Builder\Method; +use WSDL\Builder\Parameter; +use WSDL\Builder\WSDLBuilder; +use WSDL\Lexer\Tokenizer; +use WSDL\WSDL; class SamanUssd { + /** + * If disabled, annotations in soapApiClass are ignored. + * @var bool + */ + public $useAnnotions = false; + /** * @var string */ protected $soapApiClass = '\Nikapps\SamanUssd\Soap\SamanSoapServer'; /** + * Ignored if useAnnotions = true * @var string */ protected $endpoint = 'http://example.com/webservice'; /** + * Ignored if useAnnotions = true * @var string */ protected $namespace = 'http://example.com'; + /** + * Ignored if useAnnotions = true + * @var string + */ + protected $targetNamespace = 'http://example.com/types'; + + /** + * Ignored if useAnnotions = true + * @var string + */ + protected $name = 'saman-ussd'; + /** * @var string */ @@ -54,16 +81,92 @@ class SamanUssd */ public function handle() { - $wsdl = new WSDLCreator($this->soapApiClass, $this->endpoint); - $wsdl->setNamespace($this->namespace); - isset($_GET[$this->wsdlQueryString]) - ? $wsdl->renderWSDL() - : $this->setupSoapServer($wsdl); + if ($this->useAnnotions) { + $builder =(new AnnotationWSDLBuilder($this->soapApiClass))->build()->getBuilder(); + } else { + $builder = WSDLBuilder::instance() + ->setName($this->name) + ->setTargetNamespace($this->targetNamespace) + ->setNs($this->namespace) + ->setLocation($this->endpoint) + ->setStyle(SoapBinding::RPC) + ->setUse(SoapBinding::LITERAL) + ->setSoapVersion(BindingType::SOAP_11) + ->setMethods($this->getMethods()); + } + + $wsdl = WSDL::fromBuilder($builder); + + if (isset($_GET[$this->wsdlQueryString])) + { + echo $wsdl->create(); + } else + { + $this->setupSoapServer( + $builder->getNs() . strtolower(ltrim(str_replace('\\', '/', $this->soapApiClass), '/')), + $builder->getLocation() + ); + } } /** - * Set soap api class + * Method definion (if not using annotations) + * + * @return array + * @throws \Exception + */ + private function getMethods() { + $tokenizer = new Tokenizer(); + + $meths=[]; + /** + * public function GetProductInfo + */ + $parameters1 = [ + Parameter::fromTokens($tokenizer->lex('string $productCode')), + Parameter::fromTokens($tokenizer->lex('string $languageCode')) + ]; + $return1 = Parameter::fromTokens($tokenizer->lex('string $Result')); + $meths[] = new Method('GetProductInfo', $parameters1, $return1); + /** + * public function CallSaleProvider + * + */ + + $parameters2 = [ + Parameter::fromTokens($tokenizer->lex('string $productCode')), + Parameter::fromTokens($tokenizer->lex('int $Amount')), + Parameter::fromTokens($tokenizer->lex('string $CellNumber')), + Parameter::fromTokens($tokenizer->lex('long $SEPId')), + Parameter::fromTokens($tokenizer->lex('string $languageCode')) + ]; + $return2 = Parameter::fromTokens($tokenizer->lex('string $Result')); + $meths[] = new Method('CallSaleProvider', $parameters2, $return2); + /* + * public function ExecSaleProvider + */ + + $parameters3 = [Parameter::fromTokens($tokenizer->lex('string $ProviderID'))]; + $return3 = Parameter::fromTokens($tokenizer->lex('string $Result')); + $meths[] = new Method('ExecSaleProvider', $parameters3, $return3); + + /** + * public function CheckStatus + */ + $parameters4 = [Parameter::fromTokens($tokenizer->lex('string $ProviderID'))]; + $return4 = Parameter::fromTokens($tokenizer->lex('string $Result')); + $meths[] = new Method('CheckStatus', $parameters4, $return4); + + return $meths; + } + /** + * Set soap api class. + * + * IF $this->useAnnotations = true, THEN annotations in $soapApiClass WILL override + * $this->name, $this->targetNamespace, $this->namespace and $this->endpoint. + * + * To ignore annotation in $soapApiClass, set $this->useAnnotations = false. * * @param string $soapApiClass * @return $this @@ -76,7 +179,7 @@ public function setSoapApiClass($soapApiClass) } /** - * Set api endpoint + * Set api endpoint (if not using annotations) * * @param string $endpoint * @return $this @@ -89,7 +192,7 @@ public function endpoint($endpoint) } /** - * Set namespace + * Set namespace (if not using annotations) * * @param string $namespace * @return $this @@ -101,6 +204,20 @@ public function setNamespace($namespace) return $this; } + /** + * Set target-namespace (if not using annotations) + * + * @param string $namespace + * @return $this + */ + public function setTargetNamespace($namespace) + { + $this->targetNamespace = $namespace; + + return $this; + } + + /** * Set wsdl query string * @@ -191,20 +308,19 @@ public function onCheckStatus(\Closure $callback) } /** - * Setup soap server - * - * @param WSDLCreator $wsdl + * @param string $uri + * @param string $location */ - protected function setupSoapServer(WSDLCreator $wsdl) + protected function setupSoapServer(string $uri, string $location) { $request = file_get_contents('php://input'); $server = new SoapServer( - $this->endpoint . '?' . $this->wsdlQueryString, + $location . '?' . $this->wsdlQueryString, array_merge([ - 'uri' => $wsdl->getNamespaceWithSanitizedClass(), + 'uri' => $uri, 'cache_wsdl' => WSDL_CACHE_NONE, - 'location' => $wsdl->getLocation(), + 'location' => $location, 'style' => SOAP_RPC, 'use' => SOAP_LITERAL ], $this->options) diff --git a/src/Soap/SamanSoapServer.php b/src/Soap/SamanSoapServer.php index ef06b0e..bb6bc18 100644 --- a/src/Soap/SamanSoapServer.php +++ b/src/Soap/SamanSoapServer.php @@ -3,7 +3,25 @@ use Nikapps\SamanUssd\Contracts\SamanSoapApi; use Nikapps\SamanUssd\Contracts\SamanUssdListener; +use WSDL\Annotation\BindingType; +use WSDL\Annotation\SoapBinding; +use WSDL\Annotation\WebMethod; +use WSDL\Annotation\WebParam; +use WSDL\Annotation\WebResult; +use WSDL\Annotation\WebService; +/** + * Class SamanSoapServer + * + * @WebService( + * name="SamanSoapServer", + * targetNamespace="saman-ussd.dev/nikapps/samanussd/soap/samansoapserver", + * location="http://saman-ussd.dev/tests/api/webservice.php", + * ns="saman-ussd.dev/nikapps/samanussd/soap/samansoapserver/types" + * ) + * @BindingType(value="SOAP_11") + * @SoapBinding(style="RPC", use="LITERAL") + */ class SamanSoapServer implements SamanSoapApi { /** @@ -44,9 +62,19 @@ public function setListener($listener) /** * Get product info * - * @WebMethod - * @param string $productCode - * @param string $languageCode + * @WSDL\Annotation\WebMethod + * + * @WSDL\Annotation\WebParam( + * param="string $productCode" + * ) + * + * @WSDL\Annotation\WebParam( + * param="string $languageCode" + * ) + * + * @WSDL\Annotation\WebResult( + * param="string $Result" + * ) * @return string $Result */ public function GetProductInfo($productCode, $languageCode) @@ -65,13 +93,31 @@ public function GetProductInfo($productCode, $languageCode) /** * Notify sale provider * - * @WebMethod - * @param string $productCode - * @param int $Amount - * @param string $CellNumber - * @param long $SEPId - * @param string $languageCode - * @return string $Result + * @WSDL\Annotation\WebMethod + * + * @WSDL\Annotation\WebParam( + * param="string $productCode" + * ) + * + * @WSDL\Annotation\WebParam( + * param="int $Amount" + * ) + * + * @WSDL\Annotation\WebParam( + * param="string $CellNumber" + * ) + * + * @WSDL\Annotation\WebParam( + * param="long $SEPId" + * ) + * + * @WSDL\Annotation\WebParam( + * param="string $languageCode" + * ) + * + * @WSDL\Annotation\WebResult( + * param="string $Result" + * ) */ public function CallSaleProvider( $productCode, @@ -103,8 +149,15 @@ public function CallSaleProvider( /** * Confirm payment * - * @WebMethod - * @param string $ProviderID + * @WSDL\Annotation\WebMethod + * + * @WSDL\Annotation\WebParam( + * param="string $ProviderID" + * ) + * + * @WSDL\Annotation\WebResult( + * param="string $Result" + * ) * @return string $Result */ public function ExecSaleProvider($ProviderID) @@ -119,8 +172,15 @@ public function ExecSaleProvider($ProviderID) /** * Check status of transaction * - * @WebMethod - * @param string $ProviderID + * @WSDL\Annotation\WebMethod + * + * @WSDL\Annotation\WebParam( + * param="string $ProviderID" + * ) + * + * @WSDL\Annotation\WebResult( + * param="string $Result" + * ) * @return string $Result */ public function CheckStatus($ProviderID) diff --git a/tests/api/ApiTester.php b/tests/api/ApiTester.php index 7c1a9ea..8d21ccc 100644 --- a/tests/api/ApiTester.php +++ b/tests/api/ApiTester.php @@ -1,4 +1,4 @@ -setHeader('X-Requested-With', 'Codeception'); - * $I->amOnPage('test-headers.php'); - * ?> - * ``` - * - * @param string $name the name of the request header - * @param string $value the value to set it to for subsequent - * requests * @see \Codeception\Module\PhpBrowser::setHeader() */ - public function setHeader($name, $value) { + public function setHeader($header, $value) { return $this->scenario->runStep(new \Codeception\Step\Action('setHeader', func_get_args())); } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Deletes the header with the passed name. Subsequent requests - * will not have the deleted header in its request. - * - * Example: - * ```php - * setHeader('X-Requested-With', 'Codeception'); - * $I->amOnPage('test-headers.php'); - * // ... - * $I->deleteHeader('X-Requested-With'); - * $I->amOnPage('some-other-page.php'); - * ?> - * ``` - * - * @param string $name the name of the header to delete. - * @see \Codeception\Module\PhpBrowser::deleteHeader() - */ - public function deleteHeader($name) { - return $this->scenario->runStep(new \Codeception\Step\Action('deleteHeader', func_get_args())); - } - - /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -846,240 +808,6 @@ public function dontSeeInField($field, $value) { } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function canSeeInFormFields($formSelector, $params) { - return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function seeInFormFields($formSelector, $params) { - return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInFormFields', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function cantSeeInFormFields($formSelector, $params) { - return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function dontSeeInFormFields($formSelector, $params) { - return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInFormFields', func_get_args())); - } - - /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -1133,52 +861,7 @@ public function dontSeeInFormFields($formSelector, $params) { * $I->submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true))); * * ``` - * - * Pair this with seeInFormFields for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * Parameter values can be set to arrays for multiple input fields - * of the same name, or multi-select combo boxes. For checkboxes, - * either the string value can be used, or boolean values which will - * be replaced by the checkbox's value in the DOM. - * - * ``` php - * submitForm('#my-form', [ - * 'field1' => 'value', - * 'checkbox' => [ - * 'value of first checkbox', - * 'value of second checkbox, - * ], - * 'otherCheckboxes' => [ - * true, - * false, - * false - * ], - * 'multiselect' => [ - * 'first option value', - * 'second option value' - * ] - * ]); - * ?> - * ``` * - * Mixing string and boolean values for a checkbox's value is not - * supported and may produce unexpected results. - * * @param $selector * @param $params * @param $button @@ -1435,7 +1118,6 @@ public function grabValueFrom($field) { * [!] Method is generated. Documentation taken from corresponding module. * * Sets a cookie with the given name and value. - * You can set additional cookie params like `domain`, `path`, `expire`, `secure` in array passed as last argument. * * ``` php * * ``` * - * @param $name - * @param $val - * @param array $params - * @internal param $cookie - * @internal param $value + * @param $cookie + * @param $value * * @return mixed * @see \Codeception\Lib\InnerBrowser::setCookie() */ - public function setCookie($name, $val, $params = null) { + public function setCookie($name, $val) { return $this->scenario->runStep(new \Codeception\Step\Action('setCookie', func_get_args())); } @@ -1461,15 +1140,13 @@ public function setCookie($name, $val, $params = null) { * [!] Method is generated. Documentation taken from corresponding module. * * Grabs a cookie value. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. * * @param $cookie * - * @param array $params * @return mixed * @see \Codeception\Lib\InnerBrowser::grabCookie() */ - public function grabCookie($name, $params = null) { + public function grabCookie($name) { return $this->scenario->runStep(new \Codeception\Step\Action('grabCookie', func_get_args())); } @@ -1478,7 +1155,6 @@ public function grabCookie($name, $params = null) { * [!] Method is generated. Documentation taken from corresponding module. * * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. * * ``` php * scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. * * ``` php * scenario->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args())); } @@ -1521,31 +1196,27 @@ public function seeCookie($name, $params = null) { * [!] Method is generated. Documentation taken from corresponding module. * * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. * * @param $cookie * - * @param array $params * @return mixed * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() */ - public function cantSeeCookie($name, $params = null) { + public function cantSeeCookie($name) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. * * @param $cookie * - * @param array $params * @return mixed * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() */ - public function dontSeeCookie($name, $params = null) { + public function dontSeeCookie($name) { return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args())); } @@ -1554,15 +1225,13 @@ public function dontSeeCookie($name, $params = null) { * [!] Method is generated. Documentation taken from corresponding module. * * Unsets cookie with the given name. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. * * @param $cookie * - * @param array $params * @return mixed * @see \Codeception\Lib\InnerBrowser::resetCookie() */ - public function resetCookie($name, $params = null) { + public function resetCookie($name) { return $this->scenario->runStep(new \Codeception\Step\Action('resetCookie', func_get_args())); } @@ -2457,6 +2126,7 @@ public function grabDataFromResponseByJsonPath($jsonPath) { * This assertion allows you to check the structure of response json. * * * ```json + * ```json * { "store": { * "book": [ * { "category": "reference", @@ -2504,6 +2174,7 @@ public function canSeeResponseJsonMatchesXpath($xpath) { * This assertion allows you to check the structure of response json. * * * ```json + * ```json * { "store": { * "book": [ * { "category": "reference", @@ -2643,31 +2314,6 @@ public function seeResponseJsonMatchesJsonPath($jsonPath) { } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Opposite to seeResponseJsonMatchesJsonPath - * - * @param array $jsonPath - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\REST::dontSeeResponseJsonMatchesJsonPath() - */ - public function cantSeeResponseJsonMatchesJsonPath($jsonPath) { - return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeResponseJsonMatchesJsonPath', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Opposite to seeResponseJsonMatchesJsonPath - * - * @param array $jsonPath - * @see \Codeception\Module\REST::dontSeeResponseJsonMatchesJsonPath() - */ - public function dontSeeResponseJsonMatchesJsonPath($jsonPath) { - return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeResponseJsonMatchesJsonPath', func_get_args())); - } - - /** * [!] Method is generated. Documentation taken from corresponding module. * diff --git a/tests/api/ExecSaleProviderCept.php b/tests/api/ExecSaleProviderCept.php index 6b12023..9db70d0 100644 --- a/tests/api/ExecSaleProviderCept.php +++ b/tests/api/ExecSaleProviderCept.php @@ -3,7 +3,7 @@ $I = new ApiTester($scenario); $I->am('Saman IT employee!'); -$I->wantTo('test CallSaleProvider method on this webservice'); +$I->wantTo('test ExecSaleProvider method on this webservice'); $I->haveHttpHeader('Content-Type', 'text/xml'); $I->sendPOST(