Retrieve class property value dynamically - php

I'm coming from a .Net background and I'm trying to wrap my brain around a programming pattern that I'm used to but in PHP.
I've got a class with an associative array property. I'd like to "elevate" some, but not all, of the associative array keys to class-level properties. In C# I'd normally do something like this:
//C# Code
class MyClass{
private Dictionary<string, string> attributes = new Dictionary<string,string>();
//Get/Set the ID from the private store
public string ID{
get { return (attributes.ContainsKey("ID") ? attributes["ID"] : "n/a"); }
set { attributes.Add("ID", value); }
}
}
This allows my object to control default values for missing properties. In PHP I couldn't find any way to do this directly. My first workaround was to just use functions:
//PHP Code
class MyClass{
private $attributes = array();
//Get the ID
public function getID(){
return (array_key_exists('ID', $this->attributes) ? $this->attributes['ID'] : 'n/a');
}
//Set the ID
public function setID($value){
$this->attributes['ID'] = $value;
}
}
This works although the calling syntax is slightly different and I've got two methods per property. Also, the code that is consuming these objects is currently inspecting object variables so functions wouldn't be found.
Then I started going down the magic method paths of __set and __get on the object itself and just switch case on the $name that's passed in and setting/getting my local variables. Unfortunately these methods don't get invoked if you modify the underlying array directly.
So my question is, is it possible in PHP to have a class-level property/variable that doesn't get calculated until it gets used?

PHP doesn't have properties as C# programmers would understand the concept, so you'll have to use methods as the getter and setter, but the principle is exactly the same.
class MyClass {
private $attributes = array ();
public function getSomeAttribute () {
if (!array_key_exists ('SomeAttribute', $this -> attributes)) {
// Do whatever you need to calculate the value of SomeAttribute here
$this -> attributes ['SomeAttribute'] = 42;
}
return $this -> attributes ['SomeAttribute'];
}
// or if you just want a predictable result returned when the value isn't set yet
public function getSomeAttribute () {
return array_key_exists ('SomeAttribute', $this -> attributes)?
$this -> attributes ['SomeAttribute']:
'n/a';
}
public function setSomeAttribute ($value) {
$this -> attributes ['SomeAttribute'] = $value;
}
}
You essentially got the basic ideas right with your implementation, but it does mean a lot of "boilerplate" code. In theory you can avoid a lot of that with __get and __set, but I'd strongly advise against those as they can lead to epic amounts of confusion and nasty logical tangles like the "what happens if the value is set within the class instead of from outside?" issue that you've run into.

In PHP You can do something like this:
<?php
class MyClassWithoutParams{
function test(){
return $this->greeting . " " . $this->subject;
}
}
$class = new MyClassWithoutParams();
$class->greeting = "Hello";
$class->subject = "world";
echo $class->greeting . " " . $class->subject . "<br />"; //Hello World
echo $class->test(). "<br />"; //Hello World
?>
You don't need to define any property, getter or setter to use properties. Of course it is better to do so, because it makes the code easier to read and allows autocomplete-functions of eclipse (or whatever IDE) to understand what you are looking for - but if you are just looking for the laziest way to implement an entity, that would work as well.
A more common approach would be to use an associative array instead of a class - but on arrays you cant define methods.
<?php
$array = new array();
$array["greeting"] = "Hello";
$array["subject"] = "World";
echo $array["greeting"] . " " . $array["subject"]; //Output: Hello World
?>

Related

PHP - Call object methods dynamically via variables

I am trying to unit-test some of my code and it would be easier to just call my setters dynamically based on some variables. Unfortunately my approach does not work as expected and I couldn't find more information regarding on how to do that.
I have one variable which always is a string. It is used as property name and together with the "set" keyword it should result in "setSomething" or "setSomethingElse".
I already tried
$obj->set{$property}($value);
// or
$obj->set$property($value);
But those do not seem to work.
Maybe someone of you pro's know the right approach ;)!
You need to make the entire method name a variable, or enclose the whole name in {} e.g.
class test {
public $Something;
public $SomethingElse;
function setSomething($value) {
$this->Something = $value;
}
function setSomethingElse($value) {
$this->SomethingElse = $value;
}
}
$property = "Something";
$t = new test;
$setter = "set$property";
$t->$setter(4);
echo $t->Something;
$property = "SomethingElse";
$t->{"set$property"}(8);
echo $t->SomethingElse;
Output
4
8
Demo on 3v4l.org

PHP : Calling classes - Whats the diference? [duplicate]

There are two distinct ways to access methods in PHP, but what's the difference?
$response->setParameter('foo', 'bar');
and
sfConfig::set('foo', 'bar');
I'm assuming -> (dash with greater than sign or chevron) is used for functions for variables, and :: (double colons) is used for functions for classes. Correct?
Is the => assignment operator only used to assign data within an array? Is this in contrast to the = assignment operator which is used to instantiate or modify a variable?
When the left part is an object instance, you use ->. Otherwise, you use ::.
This means that -> is mostly used to access instance members (though it can also be used to access static members, such usage is discouraged), while :: is usually used to access static members (though in a few special cases, it's used to access instance members).
In general, :: is used for scope resolution, and it may have either a class name, parent, self, or (in PHP 5.3) static to its left. parent refers to the scope of the superclass of the class where it's used; self refers to the scope of the class where it's used; static refers to the "called scope" (see late static bindings).
The rule is that a call with :: is an instance call if and only if:
the target method is not declared as static and
there is a compatible object context at the time of the call, meaning these must be true:
the call is made from a context where $this exists and
the class of $this is either the class of the method being called or a subclass of it.
Example:
class A {
public function func_instance() {
echo "in ", __METHOD__, "\n";
}
public function callDynamic() {
echo "in ", __METHOD__, "\n";
B::dyn();
}
}
class B extends A {
public static $prop_static = 'B::$prop_static value';
public $prop_instance = 'B::$prop_instance value';
public function func_instance() {
echo "in ", __METHOD__, "\n";
/* this is one exception where :: is required to access an
* instance member.
* The super implementation of func_instance is being
* accessed here */
parent::func_instance();
A::func_instance(); //same as the statement above
}
public static function func_static() {
echo "in ", __METHOD__, "\n";
}
public function __call($name, $arguments) {
echo "in dynamic $name (__call)", "\n";
}
public static function __callStatic($name, $arguments) {
echo "in dynamic $name (__callStatic)", "\n";
}
}
echo 'B::$prop_static: ', B::$prop_static, "\n";
echo 'B::func_static(): ', B::func_static(), "\n";
$a = new A;
$b = new B;
echo '$b->prop_instance: ', $b->prop_instance, "\n";
//not recommended (static method called as instance method):
echo '$b->func_static(): ', $b->func_static(), "\n";
echo '$b->func_instance():', "\n", $b->func_instance(), "\n";
/* This is more tricky
* in the first case, a static call is made because $this is an
* instance of A, so B::dyn() is a method of an incompatible class
*/
echo '$a->dyn():', "\n", $a->callDynamic(), "\n";
/* in this case, an instance call is made because $this is an
* instance of B (despite the fact we are in a method of A), so
* B::dyn() is a method of a compatible class (namely, it's the
* same class as the object's)
*/
echo '$b->dyn():', "\n", $b->callDynamic(), "\n";
Output:
B::$prop_static: B::$prop_static value
B::func_static(): in B::func_static
$b->prop_instance: B::$prop_instance value
$b->func_static(): in B::func_static
$b->func_instance():
in B::func_instance
in A::func_instance
in A::func_instance
$a->dyn():
in A::callDynamic
in dynamic dyn (__callStatic)
$b->dyn():
in A::callDynamic
in dynamic dyn (__call)
:: is used in static context, ie. when some method or property is declared as static:
class Math {
public static function sin($angle) {
return ...;
}
}
$result = Math::sin(123);
Also, the :: operator (the Scope Resolution Operator, a.k.a Paamayim Nekudotayim) is used in dynamic context when you invoke a method/property of a parent class:
class Rectangle {
protected $x, $y;
public function __construct($x, $y) {
$this->x = $x;
$this->y = $y;
}
}
class Square extends Rectangle {
public function __construct($x) {
parent::__construct($x, $x);
}
}
-> is used in dynamic context, ie. when you deal with some instance of some class:
class Hello {
public function say() {
echo 'hello!';
}
}
$h = new Hello();
$h->say();
By the way: I don't think that using Symfony is a good idea when you don't have any OOP experience.
Actually by this symbol we can call a class method that is static and not be dependent on other initialization...
class Test {
public $name;
public function __construct() {
$this->name = 'Mrinmoy Ghoshal';
}
public static function doWrite($name) {
print 'Hello '.$name;
}
public function write() {
print $this->name;
}
}
Here the doWrite() function is not dependent on any other method or variable, and it is a static method. That's why we can call this method by this operator without initializing the object of this class.
Test::doWrite('Mrinmoy');
// Output: Hello Mrinmoy.
But if you want to call the write method in this way, it will generate an error because it is dependent on initialization.
The => operator is used to assign key-value pairs in an associative array. For example:
$fruits = array(
'Apple' => 'Red',
'Banana' => 'Yellow'
);
It's meaning is similar in the foreach statement:
foreach ($fruits as $fruit => $color)
echo "$fruit is $color in color.";
The difference between static and instantiated methods and properties seem to be one of the biggest obstacles to those just starting out with OOP PHP in PHP 5.
The double colon operator (which is called the Paamayim Nekudotayim from Hebrew - trivia) is used when calling an object or property from a static context. This means an instance of the object has not been created yet.
The arrow operator, conversely, calls methods or properties that from a reference of an instance of the object.
Static methods can be especially useful in object models that are linked to a database for create and delete methods, since you can set the return value to the inserted table id and then use the constructor to instantiate the object by the row id.
Yes, I just hit my first 'PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM'. My bad, I had a $instance::method() that should have been $instance->method(). Silly me.
The odd thing is that this still works just fine on my local machine (running PHP 5.3.8) - nothing, not even a warning with error_reporting = E_ALL - but not at all on the test server, there it just explodes with a syntax error and a white screen in the browser. Since PHP logging was turned off at the test machine, and the hosting company was too busy to turn it on, it was not too obvious.
So, word of warning: apparently, some PHP installations will let you use a $instance::method(), while others don't.
If anybody can expand on why that is, please do.

How to Pass a function to a class in php

I have a class that generates data based on a few things. I would like to format that data from the outside. So I am trying to pass a function into the class so that it would format that data. I have looked at many examples, but it seems this is unique.
Can anybody give an idea of how to do this? The following code gives an error.
<?php
class someClass {
var $outsideFunc; // placeholder for function to be defined from outside
var $somevar='Me'; // generated text
function echoarg($abc){
$outsideFunc=$this->outsideFunc; // bring the outside function in
call_user_func($outsideFunc,$abc); // execute outside function on text
echo $abc;
}
}
function outsidefunc($param){ // define custom function
$param='I am '.$param;
}
$someClass=new someClass();
$someClass -> outsideFunc = 'outsideFunc'; // send custom function into Class
$someClass -> echoarg($someClass->somevar);
$someClass -> outsidefunc = 'outsidefunc';
In PHP, function names are not case sensitive, yet object property names are. You need $someClass->outsideFunc, not $someClass->outsidefunc.
Note that good OOP design practice calls for the use of getter and setter methods rather than just accessing properties directly from outside code. Also note that PHP 5.3 introduced support for anonymous functions.
Yeah. You are right. Now there is no error. But it does not work either.
By default, PHP does not pass arguments by reference; outsidefunc() does not actually do anything useful. If you want it to set $param in the caller to something else, and do not want to just return the new value, you could change the function signature to look like this:
function outsidefunc(&$param) {
You would also need to change the way you call the function, as call_user_func() does not allow you to pass arguments by reference. Either of these ways should work:
$outsideFunc($abc);
call_user_func_array($outsideFunc, array(&$abc));
Why not pass your function as an argument?
<?php
class someClass {
public $somevar="Me";
public function echoarg($abc,$cb=null) {
if( $cb) $cb($abc);
echo $abc;
}
}
$someClass = new someClass();
$someClass->echoarg($someClass->somevar,function(&$a) {$a = "I am ".$a;});
i am not sure what exactly you are looking for, but what i get is, you want to pass object in a function which can be acheive by
Type Hinting in PHP.
class MyClass {
public $var = 'Hello World';
}
function myFunction(MyClass $foo) {
echo $foo->var;
}
$myclass = new MyClass;
myFunction($myclass);
OP, perhaps closures are what you're looking for?
It doesn't do EXACTLY what you're looking for (actually add function to class), but can be added to a class variable and executed like any normal anonymous function.
$myClass->addFunc(function($arg) { return 'test: ' . $arg });
$myClass->execFunc(0);
class myClass {
protected $funcs;
public function addFunc(closure $func) {
$this->funcs[] = $func;
}
public function execFunc($index) { $this->funcs[$index](); } // obviously, do some checking here first.
}

assigning static var to non-static var, method to return the values not working

I'm new to PHP and practicing using static variables. I decided to grab an example that I learnt from C++ and re-write it for PHP (example from the bottom of this article).
There's a class with two private variables (one static), a constructor and a get-method. The constructor assigns the static variable's value to the second private variable, and then increments.
<?php
class Something
{
private static $s_nIDGenerator = 1;
private $m_nID;
public function Something() {
$m_nID = self::$s_nIDGenerator++;
echo "m_nID: " . $m_nID . "</br>"; //for testing, can comment this out
}
public function GetID() {
return $m_nID;
}
}
// extra question:
// static variable can be assigned a value outside the class in C++, why not in PHP?
// Something::$s_nIDGenerator = 1;
$cFirst = new Something();
$cSecond = new Something();
$cThird = new Something();
echo $cFirst->GetID() . "</br>";
echo $cSecond->GetID() . "</br>";
echo $cThird->GetID() . "</br>";
?>
Using the echo test in line 9 to see if m_nID is getting a value I see:
m_nID: 1
m_nID: 2
m_nID: 3
But these values are not being returned by the "->GetID()" calls. Any ideas why?
Edit: both replies so far have solved this, I wish I could "check" them both, so thank you! I'll leave the original code in the question as-is for any future people who have a similar problem
Your background in C++ led up to this issue, which is an easy mistake to make. In PHP, all instance (or object) variables are referenced using $this->, and static (or class) variables with self::. Based on your code:
public function GetID() {
return $m_nID;
}
Access to the private variable $m_nID should be scoped like this:
public function GetID() {
return $this->m_nID;
}
And inside your constructor:
$m_nID = self::$s_nIDGenerator++;
It should have been:
$this->m_nID = self::$s_nIDGenerator++;
Q & A
Why is there no need to put $ before m_nID when using $this->
The above two ways of referencing instance and class variables come with a very different kind of syntax:
$this is the instance reference variable and any properties are accessed using the -> operator; the $ is not repeated for the property names themselves, although they're present in the declaration (e.g. private $myprop).
self:: is synonymous to Something:: (the class name itself); it doesn't reference an instance variable and therefore has no $ in front of it. To differentiate static variables from class constants (self::MYCONST) and class methods (self::myMethod()) it's prefixed with a $.
Extra
That said, $this->$myvar is accepted too and works like this:
private $foo = 'hello world';
function test()
{
$myvar = 'foo';
echo $this->$foo; // echoes 'hello world'
}
class Something{
private static $s_nIDGenerator = 1;
private $m_nID;
public function Something() {
$this->m_nID = self::$s_nIDGenerator++;
}
public function GetID() {
return $this->m_nID;
}
}
It is interesting to note the difference between using self::$s_nIDGenerator on a static variable vs using $this->s_nIDGenerator on a static variable, whereas $this-> will not store anything.

PHP Multiple Object Dereference

How can I do multiple levels of de-referencing? For instance, in C#, we can keep appending a '.' to access the next objects properties: string s, s.ToString(), s.ToString().ToUpper(). With PHP, I get to around $this->someobject, but $this->someobject->somethingelse does not appear to work.
Any ideas?
Assuming you're using PHP5+, and $this->someobject returns an object with a property called somethingelse; it should work.
Similarly, this also works
class Example
{
public function foo()
{
echo 'Hello';
return $this; // returning an object (self)
}
public function bar()
{
echo ' World';
return $this;
}
}
$example = new Example;
$example->foo()->bar(); // Hello World
$example->foo()->foo()->foo()->bar()->foo(); // HelloHelloHello WorldHello
Edit:
Just as a further note, you don't have to return self. Any object will suffice.
class Example1
{
public function __construct(Example2 $example2)
{
$this->example2 = $example2;
$this->example2->setExample1($this);
}
public function foo()
{
echo 'Hello';
return $this->example2;
}
}
class Example2
{
public function setExample1(Example1 $example1)
{
$this->example1 = $example1;
}
public function bar()
{
echo ' World';
return $this->example1;
}
}
$example = new Example1(new Example2());
$example->foo()->bar(); // Hello World
$example->foo()->bar()->foo()->bar(); // Hello WorldHello World
PHP's dereference operator only works on Objects; and, unlike some other languages, only Objects are objects ;) Primitives don't have an implicit wrapper. So, if $this->someobject resolves to a non object, (like a string, float, int or array), you cannot chain the dereference operator any further.
You have the syntax correct, but only for nested objects. Frequently, an object property might be of the array type, so you would need to access it accordingly: $this->somearray['somekey'];
If you're looking to create a fluent interface to allow for chaining, the object methods would need to return $this; in order to facilitate that.
Classes can also be accessed with the :: (scope resolution operator) without being instantiated, but that's a little outside of the scope of the question.
Hope that helps!

Categories