Can anyone tell me the advantage of using the classmap option within PHP Soapclient? Maybe with some practical examples?
The classmap option can be used to map some WSDL types to PHP classes.
Example,
class MyLoginResult {
protected $serverUrl;
protected $sessionId;
public function getServerUrl()
{
return $this->serverUrl;
}
public function getSessionId()
{
return $this->sessionId;
}
public function getServerInstance()
{
$match = preg_match(
'/https:\/\/(?<instance>[^-]+)\.example\.com/',
$this->serverUrl,
$matches
);
return $matches['instance'];
}
}
$client = new SoapClient("books.wsdl",
array('classmap' => array('LoginResult' => "MyLoginResult")));
$loginResult = $client->getLoginResult();
$instance = $loginResult->getServerInstance();
As addition to the comment by hoangthienan, I would show one more advantage when using a mapped class.
E.g. you could extend the class by a __set() method, that would be triggered when the SoapClient passes its data to the mapped class (you should know, the method will not be triggered if your property is public).
In that case you can alternate the data passed from SoapClient before you assign it to your Data-Class.
class MyLoginResult {
protected $serverUrl;
protected $sessionId;
private $is_logged_in;
public function __set($name, $value) {
if ($name == 'login_status') {
$this->is_logged_in = ($value == 'logged_in') ? true : false;
} else {
$this->$name = $value;
}
}
public function loginSuccessfull() {
return $this->is_logged_in;
}
// class code from hoangthienan
}
e.g. in this example we get a string from Soap, but we store a bool-value in our class.
You could use this for other changes to e.g. if you like to store your internal variables in a array instead of using direct properties.
Related
I'm using the latest of the Lucid architecture approach. Now I want to test a Feature with a few Jobs and Operations.
I'm also using value objects. I've rolled my own but followed some best practices. It shouldn't matter since Jobs can accept any class in both the constructor and the handle() method.
If I pass a value object to the constructor, testing the Job calls from the Feature are failing.
I mock the Job with use of the mock() static method that comes with the provided Job unit from which I extend the other jobs. I pass the arguments correct, otherwise it would fail. So that's working.
But the test fails when I pass a value object and the UnitDispatcher (part of Lucid) detects it's about a unit test.
Here's the Job:
class RetrieveMessageJob extends Job
{
private UniqueId $uniqueId;
public function __construct(UniqueId $uniqueId)
{
$this->uniqueId = $uniqueId;
}
public function handle(ServiceProvider $provider)
{
return $provider->retrieveMessage($this->uniqueId);
}
}
This is the value object:
class UniqueId
{
public string $value;
public function __construct($value)
{
$this->value = $value;
}
public function __toString()
{
return (string) $this->value;
}
public function value()
{
return (string) $this->value;
}
}
Since UnitDispatcher is using reflection to resolve the properties to compare, it tries to find the value of the property in a different way. None the less, it tries to compare some values in the
getConstructorExpectationsForInstance() method:
public function getConstructorExpectationsForInstance($unit)
{
foreach ($this->constructorExpectations as $index => $args) {
$expected = new $unit(...$args);
$ref = new ReflectionClass($unit);
// we start by assuming that the unit instance and the $expected one are equal
// until proven otherwise when we find differences between properties.
$isEqual = true;
foreach ($ref->getProperties() as $property) {
if ($property->getValue($unit) !== $property->getValue($expected)) {
$isEqual = false;
break;
}
}
if ($isEqual) {
return $this->constructorExpectations[$index];
}
}
}
Both $property->getValue($unit) and $property->getValue($expected) return a UniqueId object with exact the same value as expected by me. Changing the visibility of the property has no effect.
Technically the !== should not be true because of the same value. So I have no idea what else the comparison tries to find.
I'm a bit lost on why this keeps failing.
Here's the test I'm trying to pass:
class SyncMessagesFeatureTest extends TestCase
{
protected function setUp(): void
{
$this->feature = new SyncMessagesFeature();
}
public function testHandle()
{
$result = 'Irrelevant for this problem';
RetrieveMessageJob::mock(['uniqueId' => new UniqueId('uniqueId-1')])->shouldReturn($result);
$this->feature->handle();
}
}
Thanks for your time to read this.
I am trying to write a unit test for a function that immediately loads an object from a different class that uses the input to the function as a parameter. I am new to php unit testing and couldn't find anything that address my particular problem. A few leads that I had that led to no avail was using an injector, and trying to us a reflection.
The code I am trying to write a unit test for is:
public static function isUseful($item) {
$objPromo = MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
My attempt at mocking this out:
public function testIsUseful() {
$injector = $this->getMockBuilder('MyPromoCodes')
->setMethods(array('Load'))
->getMock();
$objPromo = $this->getMock('MyPromoCodes');
$objPromo->PromoType = 'very interesting promo type';
$injector->set($objPromo, 'MyPromoCodes');
$lineItem1 = $this->getDBMock('LineItem');
$this->assertTrue(MyClass::isUseful($lineItem1));
}
however this doesn't work because there is no set method for this object....
Not sure what else to try, any help would be appreciated.
I made the library that makes static classes mocking possible:
class MyClass {
public static $myPromoCodes = 'myPromoCodes';
public static function isUseful($item) {
$objPromo = self::$MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
}
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$myClass = Moka::stubClass('MyClass');
$myClass::$myPromoCodes = Moka::stubClass(null, ['::Load' => (object)[
'PromoType' => MyPromoCodes::Interesting_Promo_Type
]]);
$this->assertTrue($myClass::isUseful((object)['SavedSku' => 'SKU']);
$this->assertEquals([['SKU']], $myClass::$myPromoCodes->moka->report('::Load'));
}
}
To start with you cannot mock static method with PHPUnit. At least not with 4.x and 5.x.
I would suggest a DI approach like this:
class MyClass
{
private $promoCodesRepository;
public function __construct(MyPromoCodesRepository $promoCodesRepository)
{
$this->promoCodesRepository = $promoCodesRepository;
}
public function isUseful(MyItem $item)
{
$objPromo = $this->promoCodesRepository->Load($item->SavedSku);
// ...
}
}
Here you can easily mock the Load method.
Unfortunately the "static" approach creates a lot of issues during tests so it is better to avoid it whenever possible.
Why can't I get access to my "incSessionCount" function inside my "newSession" function?
class Session {
private $_num_session = 0;
private function incSessionCount() {
$this->_num_session++;
}
public static function newSession($key, $value) {
if( !isset( $_SESSION[$key] ) ) {
$_SESSION[$key] = $value;
$this->incSessionCount();
return true;
} else {
return false;
}
}
}
I just played around, like making incSessionCount() public and so on...
And then I thought, that it must be even accessible, when it's set to private ...
It's possible, that I missed a useful article, which should have helped me, but finally I ended up asking.
So why doesn't this work?
The problem is that your newSession is static, thus you are not supposed to call instance methods from it.
I suppose you're trying to do:
Session::newSession($key, $value);
instead of
$session = new Session();
$session->newSession($key, $value);
The error is not because you're calling a private method from within a public one, but because you're using $this instead of self.
$this special variable represents the current instance object while self represents the class itself.
If you enable error display and set error reporting level to E_ALL, you will see the problem is about using $this in a wrong context.
See below theses little modifications to do what you want, and check theses pages about
class Session {
private $_num_session = 0;
private static $inst = null;
public static function instance(){
if (!static::$inst)
static::$inst = new Session();
return static::$inst;
}
private function incSessionCount() {
$this->_num_session++;
}
public static function newSession($key, $value) {
if( !isset( $_SESSION[$key] ) ) {
$_SESSION[$key] = $value;
Session::getInstance()->incSessionCount();
return true;
} else {
return false;
}
}
}
You can look for design pattern and singleton on internet, and use magic __clone() to forbid more than one instance
I only found the german version of the documentation, I don't know why : http://de.php.net/manual/de/language.oop5.patterns.php
EDIT: Check this link about design patterns : http://www.phptherightway.com/pages/Design-Patterns.html
Remember, that static methods are binded with the class. Non-static methods are binded with the instance (when you do something like $instance = new MyClass();). But when you call something on the static context, you don't have to have any instance.
It's the same, when you want to call something on instance($this), because in static context doesn't exist any instance.
The problem is the public method is static and you are trying to use a method for an instantiated object. In a static method the $this variable refers to other static methods and properties only.
I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.
I'm using PHPUnit but find it difficult to make it create good mocks and stubs for objects used as datastore.
Example:
class urlDisplayer {
private $storage;
public function __construct(IUrlStorage $storage) { $this->storage = $storage; }
public function displayUrl($name) {}
public function displayLatestUrls($count) {}
}
interface IUrlStorage {
public function addUrl($name, $url);
public function getUrl($name);
}
class MysqlUrlStorage implements IUrlStorage {
// saves and retrieves from database
}
class NonPersistentStorage implements IUrlStorage {
// just stores for this request
}
Eg how to have PHPUnit stubs returning more than one possible value on two calls with different $names?
Edit: example test:
public function testUrlDisplayerDisplaysLatestUrls {
// get mock storage and have it return latest x urls so I can test whether
// UrlDisplayer really shows the latest x
}
In this test the mock should return a number of urls, however in the documentation I only how to return one value.
Your question is not very clear - but I assume you are asking how to use phpunit's mock objects to return a different value in different situations?
PHPUnit's mock classes allow you specify a custom function (ie: a callback function/method) - which is practically unlimited in what it can do.
In the below example, I created a mock IUrlStorage class that will return the next url in its storage each time it is called.
public function setUp()
{
parent::setUp();
$this->fixture = new UrlDisplayer(); //change this to however you create your object
//Create a list of expected URLs for testing across all test cases
$this->expectedUrls = array(
'key1' => 'http://www.example.com/url1/'
, 'key2' => 'http://www.example.net/url2/'
, 'key3' => 'http://www.example.com/url3/'
);
}
public function testUrlDisplayerDisplaysLatestUrls {
//Init
$mockStorage = $this->getMock('IUrlStorage');
$mockStorage->expects($this->any())
->method('getUrl')
->will( $this->returnCallback(array($this, 'mockgetUrl')) );
reset($this->expectedUrls); //reset array before testing
//Actual Tests
$this->assertGreaterThan(0, count($this->expectedUrls));
foreach ( $this->expectedUrls as $key => $expected ) {
$actual = $this->fixture->displayUrl($key);
$this->assertEquals($expected, $actual);
}
}
public function mockGetUrl($name)
{
$value = current($this->expectedUrls);
next($this->expectedUrls);
//Return null instead of false when end of array is reached
return ($value === false) ? null : $value;
}
Alternatively, sometimes it is easier to simply create a real class that mocks up the necessary functionality. This is especially easy with well defined and small interfaces.
In this specific case, I would suggest using the below class instead:
class MockStorage implements IUrlStorage
{
protected $urls = array();
public function addUrl($name, $url)
{
$this->urls[$name] = $url;
}
public function getUrl($name)
{
if ( isset($this->urls[$name]) ) {
return $this->urls[$name];
}
return null;
}
}
?>
Then in your unit test class you simply instantiate your fixture like below:
public function setUp() {
$mockStorage = new MockStorage();
//Add as many expected URLs you want to test for
$mockStorage->addUrl('name1', 'http://example.com');
//etc...
$this->fixture = new UrlDisplayer($mockStorage);
}