Looks like impossible do something like that:
class Service
{
public function get($argument1, $argument2)
{
echo "Arguments: $argument1, $argument2.";
}
}
class Main
{
public function __callStatic($method, $arguments)
{
$method = "get".ucfirst($method);
$arguments = implode(", ", $arguments);
$object = self::$method;
return $object->get($arguments);
}
public static function getService()
{
return new Service;
}
}
$main = new Main;
$main::Service("1", "2"); // Should output 'Arguments: 1, 2.'
The problem is the self mess everthing, with this will be possible.
Use self::$variable, self this we have a variable in the Main context.
$object = self don't work too.
$object = clone self dont' work too.
define("method", $method);
self::method will not work because self thinks is a class constant property.
this can't be used.
Is there a way to perform this?
Just pass the original $arguments directly to call_user_func_array:
return call_user_func_array(array($object, "get"), $arguments);
Then, just make a static call to Main:
Main::service("1", "2");
See it here in action: http://codepad.viper-7.com/phhqy6
Related
I've got a factory that I want to return a ::class from. However, the factory could potentially return a couple dozen different types (determined by the type of object passed into the factory), named TypeOneObject, TypeTwoObject etc. Is it possible to return the class using a variable, something like this?
$type = $myrequiredclass->getType();
return $type."Object"::class; // wanting TypeOneObject::class
It seems like no matter how I construct this return statement I always get PHP Parse error: syntax error, unexpected '::'
I know it'd be easy enough to do with a big if/then or switch but I'd like to avoid that.
Here's a more fleshed out scenario:
class TypeOneObject
{
public static function whoAreYou()
{
return 'Type One Object!';
}
}
class MyRequiredClass
{
public function getType()
{
return 'TypeOne';
}
}
class MyFactory
{
public static function getFactoryObject(MyRequiredClass $class)
{
$type = $class->getType()."Object";
return $type::class;
}
}
$object = MyFactory::getFactoryObject(new MyRequiredClass());
$object::whoAreYou();
The best way to get the class name from the $type instance is to use php get_class_methods function. This will get us all the methods inside the class instance. from there we can filter and use call_user_func to call the method and get the right values.
class TypeOneObject
{
public static function whoAreYou()
{
return 'Type One Object!';
}
}
class MyRequiredClass
{
public function getType()
{
return 'TypeOne';
}
}
class MyFactory
{
public static function getFactoryObject(MyRequiredClass $class)
{
$methods = get_class_methods($class);
$method = array_filter($methods, function($method) {
return $method == 'getType';
});
$class = new $class();
$method = $method[0];
$methodName = call_user_func([$class, $method]);
$objectName = sprintf('%sObject', $methodName);
return new $objectName;
}
}
$object = MyFactory::getFactoryObject(new MyRequiredClass());
echo $object::whoAreYou();
Output
Type One Object!
so far, I have this:
class Foo{
private $plugin_methods = array();
public function registerPlugin($caller, $method){
list($object, $caller) = explode('.', $caller);
$this->plugin_methods[$object][$caller] = $method;
}
public function _doPluginMethod($object, $name, $args){
if(isset($this->plugin_methods[$object][$name]))
return call_user_func_array($this->plugin_methods[$object][$name], $args);
throw new Exception("Method '{$name}' not defined for '{$object}'.");
}
public function __call($name, $args){
return $this->_doPluginMethod('foo', $name, $args);
}
}
and now I can do this:
$foo = new Foo();
$foo->registerPlugin('foo.my_plugin', function($something){
return $something * 1000;
});
$foo->my_plugin(3453245);
But how can I get to the $this object inside my "plugin" function?
Depends on the version of PHP you are using. As of PHP 5.4, the use of $this in anonymous functions (also called closures) is possible. Prior to that, it wasn't.
Check the changelog here: http://php.net/manual/en/functions.anonymous.php
I'm trying to dump elements of an object's private property through an anonymous function - of course I could achieve this in any number of other ways, but this highlights a PHP conundrum I can't solve off the top of my head, short of $foo = $this and using $foo - but THAT won't give me the private stuff, so... suggestions ?
Sample code:
class MyClass
{
private $payload = Array( 'a' => 'A element', 'b' => 'B element');
static $csvOrder = Array('b','a');
public function toCSV(){
$values = array_map(
function($name) use ($this) { return $this->payload[$name]; },
self::$csvOrder
);
return implode(',',$values);
}
}
$mc = new MyClass();
print $mc->toCSV();
I believe there is absolutely no way to do directly what you propose.
However, you can work around it either by making the anonymous method a class method (this is not what you asked for, but it could be a practical solution) or pulling everything you need out of $this explicitly and passing the extracted values into the function:
class MyClass
{
private $payload = Array( 'a' => 'A element', 'b' => 'B element');
static $csvOrder = Array('b','a');
public function toCSV(){
$payload = $this->payload;
$values = array_map(
function($name) use ($payload) { return $payload[$name]; },
self::$csvOrder
);
return implode(',',$values);
}
}
You can hack around the limitation by creating a wrapper that utilizes Reflection to allow you to access all properties and methods. You can use it like this then:
$self = new FullAccessWrapper($this);
function () use ($self) { /* ... */ }
Here a sample implementation of the wrapper, taken from here:
class FullAccessWrapper
{
protected $_self;
protected $_refl;
public function __construct($self)
{
$this->_self = $self;
$this->_refl = new ReflectionObject($self);
}
public function __call($method, $args)
{
$mrefl = $this->_refl->getMethod($method);
$mrefl->setAccessible(true);
return $mrefl->invokeArgs($this->_self, $args);
}
public function __set($name, $value)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
$prefl->setValue($this->_self, $value);
}
public function __get($name)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
return $prefl->getValue($this->_self);
}
public function __isset($name)
{
$value = $this->__get($name);
return isset($value);
}
}
Obviously the above implementation doesn't cover all aspects (e.g. it can't use magic properties and methods).
As you said yourself, it is private and therefore in accessible.
You can:
Pass $this->payload as a parameter to the anonymous function.
Create a method in the class and use it instead.
I'm trying to create a chaining function for working with strings that are returned from an XML file.
1 original string may have multiple replacements, some of which come from the XML file.
Here is the ugly and standard wrapped approach:
str_replace("what","is meant", str_replace("name","randomer",str_replace("blah", "hello", $string1)));
Here is the approach I'm trying to replicate (like Java):
$string1.replace("blah","hello").replace("name","randomer").replace("what","is meant");
With the above, it works easily... until I use the XML function to get the replacing string.
Here's my class:
class resources{
private static $instance, $string;
public static function getString($stringName){
# Create new instance
self::$instance = new self;
# Grabs stringName from an XML file
self::$string = $stringName;
# Return instance
var_dump(self::$instance);
return self::$instance;
}
public static function replace($replace_this, $with_this){
# Replace and return instance
self::$string = str_replace($replace_this, $with_this, self::$string);
return self::$instance;
}
public static function show(){
# Return String
return self::$string;
}
}
echo resources::getString("alpha") // alpha
->replace("lpha","bravo") // abravo
->replace("vo", resources::getString("charlie")->show()) // should be abracharlie
->show(); // charlie
I'd like it to understand why it's not working as I think it should and how it should actually work.
It seems that when I call the class again (despite var_dump saying its a seperate instance), it replaces the original text with "charlie" so I can't just replace a part of the first bit.
Thanks, Dominic
EDIT: Yes!! I have figured it out (using statics) but it seems Ryano below has an even better solution
<?php
class resources{
private static $instance, $string, $originalString;
public static function getInstance($stringName){
self::$instance = new self();
self::$originalString = $stringName;
return self::$instance;
}
public static function getString($stringName){
# Grabs stringName from an XML file
self::$string = $stringName;
return self::$instance;
}
function replace($replace_this, $with_this){
self::$originalString = str_replace($replace_this, $with_this, self::$originalString);
self::$string = self::$originalString;
return self::$instance;
}
function show(){
return self::$string;
}
}
echo resources::getInstance("alpha") // alpha
->replace("lpha","bravo") // abravo
->replace("vo", resources::getString("charlie")->show()) // should be abracharlie
->replace("lie", resources::getString("vo")->show()) // abracharvo
->show(); // abracharvo
echo "<br />";
echo resources::getInstance("randomer") // randomer
->replace("er","") // random
->replace("ran", resources::getString("")->show()) // dom
->replace("dom", resources::getString("Dom")->show()) // Dom
->show(); // Dom
echo "<br />";
echo resources::getInstance("nomster") // nomster
->replace("nom","nmo") // nmoster
->replace("nom", resources::getString("mon")->show()) // nmoster
->replace("nmo", resources::getString("mon")->show()) // monster
->show(); // monster
?>
Your problem is that everything is static. I would suggest brushing up on some object-oriented programming fundamentals.
Because everything is static, the state is shared between all invocations of the functions. In the line replace("vo", resources::getString("charlie")->show()), the nested call to resources::getString replaces the string built so far (abravo) with the argument to getString which is charlie. Then the wrapping function is called like replace("vo", "charlie"), but the value of self::$string is now charlie, which does not contain vo and therefore the final show() then returns simply charlie. If, instead of vo, you'd called it with replace("ar", resources::getString("charlie")->show()), the final show() would have instead returned chcharlielie.
You must create a class with non-static member variables and methods in order to maintain separate states.
Here's a working version:
class resources {
private $string;
public function __construct ($string) {
$this->string = $string;
}
public static function getString ($string) {
$obj = new resources($string);
return $obj;
}
public function replace ($replace_this, $with_this) {
# Replace and return instance
$this->string = str_replace($replace_this, $with_this, $this->string);
return $this;
}
public function show () {
# Return String
return $this->string;
}
}
Edit: I like the above code as the closest transition from the question's code. If I was writing something similar myself, I would simplify it further like this:
class Str {
private $str;
private function __construct ($str) {
$this->str = $str;
}
public static function with ($str) {
return new Str($str);
}
public function replace($replace_this, $with_this) {
$this->str = str_replace($replace_this, $with_this, $this->str);
return $this;
}
public function __toString () {
return $this->str;
}
}
echo Str::with('nomster')->replace('nom', 'mon') . "\n";
Now there's no need for show() and the names are a little nicer to type. Many other useful methods could be added here; any php string function you would want to chain.
When you call getString() several times, you create several instances since you call new self() in getString().
To prevent that from happening you should create a method getInstance() and use that in getString()
public static function getInstance() {
if(!self::instance) {
self::instance = new self();
}
return self::instance;
}
public static function getString() {
$instance = self::getInstance();
// use $instance here instead of self::instance
}
I have a class which contains an array of objects and has methods to return an object from that array by reference. There is also a method to unset an object from the array.
However if I have a variable that references an object from the array, and that element is unset, the variable still has a reference to it. What do I need to do in the remove method that will destroy that object for good, including references to it.
class myClass
{
public $objectList = array();
public function __construct()
{
$objectList[] = new myObject();
}
public function &getObjectByReference()
{
return $this->objectList[0];
}
public function removeObject()
{
unset($this->objectList[0]);
}
}
$myClass = new myClass();
$referencedObject = $myClass->getObjectByReference();
// this can now use the methods from myObject
$myClass-> removeObject();
// supposed to delete the object from memory. However $referencedObject is still
// able to use all the methods from myObject.
So thats the problem I am having, I need to be able to remove from the array and delete the object from memory so variables that reference that object are no longer usable.
Have you tried doing:
$referencedObject = &$myClass->getObjectByReference();
Is $referencedObject still there after putting in that &?
To create a reference from a return value both the function must return by reference (you already do so) and the assignment has to by by reference. So you should write:
$referencedObject =& $myClass->getObjectByReference();
If you do this the reference really will be destroyed.
But if you want to do destroy all variables having this object as value (and which are not references) then this is impossible. You can only remove the real references, not variables having the same value ;)
Php is working with garbage collector : if there is still a reference to the object then the object is not deleted.
unset($this->objectList[0])
Does not delete the object but the value in $this->objectList, the object still exists since he is referenced by $referencedObject.
One solution : when you delete the object, tell him he is being deleted and in that object you have a boolean "isDeleted". Then for every method of that object, check if isDeleted is true and in that case, just do nothing.
This is the nature of PHP's garbage collector. To make sure a caller doesn't maintain a reference to your object you have to ensure they can never touch the original object. Here is an idea:
class myClass
{
public $objectList = array();
public function __construct()
{
$objectList[] = new Wrapper(new myObject());
}
public function getObject()
{
return $this->objectList[0];
}
public function removeObject()
{
$this->objectList[0]->emptyWrapper();
unset($this->objectList[0]);
}
}
class Wrapper {
private $object;
public function __construct($object) {
$this->object = $object;
}
public function __call($method, $args) {
return call_user_func_array(array($this->object, $method), $args);
}
public function __get($attr) {
return $this->object->$attr;
}
public function __set($attr, $value) {
$this->object->$attr = $value;
}
public function emptyWrapper() {
$this->object = null;
}
}
You can use this wrapper idea or you can use forced indirection and handles. I might prefer using forced indirection instead; otherwise, a caller can still keep the wrapper object alive - though it is fairly cheap.
class myClass
{
public $objectList = array();
public function __construct()
{
$objectList[] = new myObject();
}
public function getObjectHandle() {
return 0;
}
public function removeObject($h)
{
unset($this->objectList[$h]);
}
public function call($h, $method, $args) {
call_user_func(array($this->objectList[$h], $method), $args);
}
public function get($h, $attr) {
return $this->objectList[$h]->$attr;
}
public function set($h, $attr, $value) {
$this->objectList[$h]->$attr = $value;
}
}
$myClass = new myClass();
$objectHandle = $myClass->getObjectHandle();
// example method call
$myClass->call($objectHandle, 'some_method', array('arg1', 'arg2'));
$myClass->removeObject($objectHandle);