Is that possible to have a single PHP SOAP server which will handle requests to several classes (services)?
If yes, could you please show an example implementation?
If not, could you please describe why?
Could you wrap the other services in a single class? Completely untested, it was just a thought...
class MySoapService
{
public function __construct()
{
$this->_service1 = new Service1();
$this->_service2 = new Service2();
}
// You could probably use __call() here and intercept any calls,
// thus avoiding the need for these declarations in the wrapper class...
public function add($a, $b)
{
return $this->_service1->add($a, $b);
}
public function sub($a, $b)
{
return $this->_service2->sub($a, $b);
}
}
class Service1
{
public function add($a, $b)
{
return $a + $b;
}
}
class Service2
{
public function sub($a, $b)
{
return $a - $b;
}
}
Another take on the same general idea (proxy class) - for php5
Uses a hash to map functions to callbacks.
class ServiceProxy {
private $map = array();
public function addMethod($name, $callback) {
if(is_callable($callback)) {
$this->map[$name] = $callback;
return true;
}
return false;
}
function __call($name, $args) {
if(isset($map[$name])) {
return call_user_func_array($map[$name], $args);
} else {
return null;
}
}
}
This class could also use the reflection API and add all pulic methods from an object, for example.
If both classes had methods with identical names (but different parameters) then you could use func_get_args() to analyze the arguments and differentiate between the methods that way.
If they take the same arguments... then you're kinda stuck.
Why can't you just use two separate Web Services?
Related
When creating a simple function, it is sometimes appropriate to encapsulate a small section of logic in a sub-function. My question is:
Assuming we will never use the calc function again, which of the following is easiest on the PHP parser when running this type of procedure?
1. A Nested Function: (PHP has to redefine calc each time:)
function doSomething($a, $b, $c) {
$calc = function($val) { /* do some calculation */ };
if($a>$c) return $calc($c);
else if($a<$b) return $calc($b);
else return $calc($c);
}
2. A Second Function: (PHP has to keep calc in global memory:)
function doSomething($a, $b, $c) {
if($a>$c) return calc($c);
else if($a<$b) return calc($b);
else return calc($c);
}
function calc($val) { /* do some calculation */ }
3. A Class: (More code, and still in global memory)
class something {
static public function doSomething($a, $b, $c) {
if($a>$c) return self::calc($c);
else if($a<$b) return self::calc($b);
else return self::calc($c);
}
static private function calc($val) { /* do some calculation */ }
}
Check this answer: anonymous function performance in PHP
As it states it usually does not make much of a difference. If you want to be sure: a lot depends on your specific PHP and server version so you will have to benchmark it.
Having said that, version 2 is more readable than version 1, but version 3 - a class with private function would be the most readable solution as your code tells other programmers clearly that calc() is not used elsewhere.
class something {
static public function doSomething($a, $b, $c) {
if($a>$c) return self::calc($c);
else if($a<$b) return self::calc($b);
else return self::calc($c);
}
static private function calc($val) { /* do some calculation */ }
}
In Javascript you can do this:
// Define a function
function logIt(a, b) { console.log(a, b); }
function somethingElse() {
// Store it in a variable
var theLog = logIt;
// Call it somewhere else *as the variable-name*
asyncCall(function() { theLog(1, 2); });
}
What I'd like to do in PHP is this:
class A
{
// Define a simple class method
protected function echoIt($a, $b) {
echo $a, $b;
}
public function doSomething(array $things) {
$theEchoFunction = $this->echoIt; // save it for the next line
// Get into a closure and pass the method as a variable
array_map(function($thing) use ($theEchoFunction) { // <-- this is the bit I'd like to do
// Call the function directly from the variable
$theEchoFunction($thing[0], $thing[1]);
}, $things);
}
}
I know that it's easy enough to just do $that = $this; then pass $that through to the closure, but that means I can't access $that->echoIt because it's protected. Is it possible to send the method itself to the closure?
This question could actually be the X/Y problem I guess. What I want to do is call the protected method from inside the closure. I'd like to just pass the method so that the closure doesn't need to know that the class has an echoIt method.
Specifically, this will do just fine* (in PHP as in Javascript):
class A
{
protected function echoIt($a, $b) {
echo $a, $b;
}
public function doSomething(array $things) {
array_map(function ($thing) {
$this->echoIt($thing[0], $thing[1]);
}, $things);
}
}
Assuming that is just a test setup and you do need to pass the callback around in a variable, the way to do that is with a callable pseudo type:
class A
{
protected function echoIt($a, $b) {
echo $a, $b;
}
public function doSomething(array $things) {
$callback = [$this, 'echoIt'];
array_map(function ($thing) use ($callback) {
$callback($thing[0], $thing[1]);
}, $things);
}
}
* Since PHP 5.4.
class Test
{
protected function echoIt($a, $b) {
echo $a, $b;
}
public function doSomething(array $things) {
$theEchoFunction = function($a, $b) {
return $this->echoIt($a, $b);
};
array_map(function($thing) use ($theEchoFunction) {
$theEchoFunction($thing[0], $thing[1]);
}, $things);
}
}
$test = new Test();
$test->doSomething(["1", "2"]);
Result
12
This works for me, I don't know if it works as you expected. But to assign the method to a variable, you'd need to make the variable callable. Thus made me think that you can create an anonymous function that is wrapper over the protected method. Then you pass that function to the closure.
PHP allows for variables to hold functions like so:
$f = function($a,$b) {
print "$a $b";
};
$f("Hello","World!"); //prints 'Hello World!'
This works just fine for me. I'm trying to pass a function into a class and set an instance variable to hold that function but with little luck:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
Am I doing something wrong here? The error is that the function doesn't exist so my guess currently is that it looks for a class function with that name (which there isn't one) and then gives up rather than looking for variables as well... how can I make it view the $this->distanceFunc as a variable?
EDIT:
So after the advice from the answers below, I found a solution which was the make a function to wrap the invocation. For example my class is now:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
private function distanceFunc($a,$b) {
$holder = $this->distanceFunc;
return $holder($a,$b);
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
and this works great. Php looks for functions first and can only tell if it is a variable by context I guess is the moral of this story.
Your code doesn't work because PHP interprets $this->distanceFunc(1,7) as a class method, but you can do the following:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func_array($this->distanceFunc, array(1, 7));
// print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
http://sandbox.onlinephpfunctions.com/code/cdc1bd6bd50f62d5c88631387ac9543368069310
In PHP, methods and properties of an object occupy separate namespaces. This is different from JavaScript, for example, where foo.bar = function() {} is a perfectly valid way of defining a method.
Consequently, $this->distanceFunc(1,7); looks for a method named distanceFunc on the current class, and the classes it inherits from, but never looks for the property which you happen to have given the same name.
One solution is to force PHP to look up a property, then execute it, e.g. $foo = $this->distanceFunc; $foo(1,7) or call_user_func($this->distanceFunc, 1, 7)
Another would be to define the magic method __call on your class, which gets run whenever a non-existent method is referenced. Something like this ought to work (I don't have an easy way to testright now):
function __call($func, $args) {
if ( property_exists($this, $func) && is_callable($this->$func) ) {
return call_user_func_array($this->$func, $args);
}
}
Note that this still isn't the same as a real method, for instance in terms of access to private properties.
It looks like you're going for a strategy pattern here. IE you want to be able to inject different methods for calculating distance? If so there is a more "sane" way to do it.
You can define an interface to the classes you will use to store the strategy method ensuring that the class will always have the method calculate() for example which would be your distance calculation function. Then in the constructor of your Clusterer class, type check against the interface in the parameter and call calculate() on the object passed in.
Looks like this:
interface Calculateable
{
public function calculate();
}
class MyDistanceCalculator implements Calculateable
{
public function calculate()
{
// Your function here
}
}
class Clusterer
{
protected $calc;
public function __construct(Calculateable $calc)
{
$this->calc = $calc;
$this->calc->calculate();
}
}
$myClusterer = new Clusterer(new MyDistanceCalculator());
Because you defined an interface, any object you pass in will have the calculate() function
In HHVM, you can do this:
<?php
class Foo
{
public function __construct()
{
$this->bar = function() { echo "Here\n"; };
($this->bar)();
}
}
new Foo();
But it's not yet supported in PHP. But, it will be in PHP 7 (there will be no release named PHP 6).
PHP doesn't have first class functions. In JavaScript if you returned a function you could do this: myFunctionThatReturnsAFunction()(1,2), but not in PHP.
<?php
class Clusterer {
private $distanceFunc;
public function __construct(Closure $f) {
$this->distanceFunc = $f;
}
public function getDistFunc()
{
return $this->distanceFunc;
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
$a = $c->getDistFunc();
echo $a(1,2);
Take a look at call_user_func
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func($this->distanceFunc, 1, 7); //works too ;)
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
Don't ask me what is the difference, but it works the way you want (One of the reasons i hate this language)
I came to know about mixins.So my doubt is, is it possible to use mixins in php?If yes then how?
Use Trait introduced in PHP 5.4
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
which prints Hello World!
http://php.net/manual/en/language.oop5.traits.php
This answer is obsolete as of PHP 5.4. See Jeanno's answer for how to use traits.
It really depends on what level of mixins you want from PHP. PHP handles single-inheritance, and abstract classes, which can get you most of the way.
Of course the best part of mixins is that they're interchangeable snippets added to whatever class needs them.
To get around the multiple inheritance issue, you could use include to pull in snippets of code. You'll likely have to dump in some boilerplate code to get it to work properly in some cases, but it would certainly help towards keeping your programs DRY.
Example:
class Foo
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
class Fizz
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
It's not as direct as being able to define a class as class Foo mixin Bar, but it should get you most of the way there. There are some drawbacks: you need to keep the same parameter names and return variable names, you'll need to pass other data that relies on context such as func_get_args_array or __FILE__.
Mixins for PHP (PHP does not implement Mixins natively, but this library will help)
First google result for "php5 mixin": http://www.sitepoint.com/forums/php-application-design-147/ruby-like-mixins-php5-332491.html
First google result for "php mixin": http://www.advogato.org/article/470.html
Short answer: yes, but not natively (yet, evidently, as #mchl notes). Check those out.
Longer answer: if you're using runkit, checkout runkit_method_copy(): "Copies a method from class to another."
I based mixins functionality on the blog entry found at jansch.nl.
class Node
{
protected $__decorator_lookup = array();
public function __construct($classes = array())
{
foreach($classes as $class)
if (class_exists($class))
{
$decorator = new $class($this);
$methods = get_class_methods($decorator);
if (is_array($methods))
foreach($methods as $method)
$this->__decorator_lookup[strtolower($method)] = $decorator;
}
else
trigger_error("Tried to inherit non-existant class", E_USER_ERROR);
}
public function __get($name)
{
switch($name)
{
default:
if ($this->__decorator_lookup[strtolower($name)])
return $this->__call($name);
}
}
public function __call($method, $args = array())
{
if(isset($this->__decorator_lookup[strtolower($method)]))
return call_user_func_array(array($this->__decorator_lookup[strtolower($method)], $method), $args);
else
trigger_error("Call to undefined method " . get_class($this) . "::$method()", E_USER_ERROR);
}
public function __clone()
{
$temp = $this->decorators;
$this->decorators = array();
foreach($temp as $decorator)
{
$new = clone($decorator);
$new->__self = $this;
$this->decorators[] = $new;
}
}
}
class Decorator
{
public $__self;
public function __construct($__self)
{
$this->__self = $__self;
}
public function &__get($key)
{
return $this->__self->$key;
}
public function __call($method, $arguments)
{
return call_user_func_array(array($this->__self, $method), $arguments);
}
public function __set($key, $value)
{
$this->__self->$key = $value;
}
}
class Pretty extends Decorator
{
public function A()
{
echo "a";
}
public function B()
{
$this->b = "b";
}
}
$a = new Node(array("Pretty"));
$a->A(); // outputs "a"
$a->B();
echo($a->b); // outputs "b"
EDIT:
As PHP clone is shallow, added __clone support.
Also, bear in mind that unset WON'T work (or at least I've not managed to make it work) within the mixin. So - doing something like unset($this->__self->someValue); won't unset the value on Node. Don't know why, as in theory it should work. Funny enough unset($this->__self->someValue); var_dump(isset($this->__self->someValue)); will produce correctly false, however accessing the value from Node scope (as Node->someValue) will still produce true. There's some strange voodoo there.
php 5.3
Is there a way to do this (viable in java for example)
(new MyClass())->myMethod();
i am receving: Parse error: syntax error, unexpected T_OBJECT_OPERATOR in D.. on line 7
Add
I really need that RFC to be implemented in the next PHP version!
http://wiki.php.net/rfc/instance-method-call
Is there a way we can subscribe to it so it can get more attention?
No, its not possible. There is a RFC for that
http://wiki.php.net/rfc/instance-method-call
But no one knows, when this will come to the userland.
Jacob mentioned the static method. There are other more or less useful methods to achieve the same
function instanciate($className, $arg1 = null) {
$args = func_get_args();
array_shift($args);
$c = new ReflectionClass($className);
return $c->newInstanceArgs($c);
}
instanciate('Classname', 1, 2, 3)->doSomething();
However, I prefer the temporary variable (like in the question).
Update:
I can swear there where an example for the temporary variable stuff in the question in the past. However, I meant this
$x = new Class;
$x->method();
where $x is the temporary variable.
That is not valid syntax. A handy way to achieve what you want is to use a static method to create the object.
In your MyClass:
public static function create() {
return new MyClass();
}
Then you can use:
MyClass::create()->myMethod();
However it is extra code that you have to maintain, if for example the constructor is changed or the class is extended. So you need to weigh up the benefits.
You can do something like this:
function chain_statements($statement1, $statement2) { return $statement2; }
class TClass { public Method() { ...; return $this; } }
$b = chain_statements($a = new TClass(), $a->Method());
... or more generalized:
function chain_statements(array $statements) { return end($statements); }
For example:
function chain_statements($statement1, $statement2) { return $statement2; }
function chain_statements2(array $statements) { return end($statements); }
class TClass
{
public $a = 0;
public function Method1() { $this->a = $this->a + 1; return $this; }
public function Method2() { $this->a = $this->a + 2; return $this; }
}
$b = chain_statements($c = new TClass(), $c->Method1()); echo($b->a);
$b = chain_statements2(array($c = new TClass(), $c->Method1(), $c->Method2())); echo($b->a);
... or even better:
function call_method($object) { return $object; }
$b = call_method(new TClass())->Method2(); echo($b->a);
Not as such. In PHP new is not an expression, but a language construct. The common workaround is to provide a static instantiation method for MyClass::get()->... use.
A more concise alternative is a hybrid factory function:
function MyClass() { return new MyClass; }
class MyClass {
...
}
Which then simplifies the instantiation to MyClass()->doSomething();
You can put it in one statement if you really wanted to. Use eval() ;p
But you probably shouldn't.
I had this same problem a while ago but I found this simple solution which is pretty readable too. I like the fact it uses only the standard PHP functions. There's no need to create any utility functions of your own.
call_user_func(
array(new ClassToInstance(), 'MethodName'),
'Method arguments', 'go here'
);
You can also use call_user_func_array to pass the arguments as an array.
call_user_func_array(
array(new ClassToInstance(), 'MethodName'),
array('Method arguments', 'go here')
);