PHP: can we access the property without declaration? - php

<?php
class A
{
public $attribute1;
function operation1()
{
echo 'operation1';
}
}
$a = new A();
$a->attribute3 = 10;
echo $a->attribute3;
when i run above script, It shows: 10
Question:
There is no declaration of attribute3 in class A? why i can still use it $a->attribute3 = 10;?

As #Hamish said ... because that is how PHP works.
Just like you can say:
$a = "hello";
and create a property in a function's scope or in the global scope you can use
$obj->a = "hello";
to create a property in the $obj instance's scope.
If this is undesired behavior you can throw an exception using the __get and __set magic methods.
class A{
public $property1;
public function __get($property){
throw new Exception($property." does not exist in ".__CLASS__);
}
public function __set($property, $value){
throw new Exception($property." does not exist in ".__CLASS__);
}
}

In short: because you can.
PHP lets you define object attributes without declaring them in the class.
It's not an uncommon feature, e.g. python:
class Foo(object):
pass
foo = Foo()
foo.bar = "Hi"
print foo.bar # "Hi"

Related

PHP property doesn't exist but no errors thrown when used

So I came across a code like this and it makes use of one of the Bar class properties called testObj while it's not defined so I expected this to be wrong but I tested it my own and no errors:
<?php
class Foo{
public function __construct()
{
echo 'Echo From Foo';
}
}
class Bar{
public function __construct(Foo $foo)
{
$this->testObj = $foo;
}
}
$bar = new Bar(new Foo);
Why is that so? Does this have anything to do with the "Dynamic/Loose Type"ness of PHP or something else?
Properties can be defined dynamically and their visibility is defaulted to public as can be seen in the example below:
class X {
public function test()
{
$this->y = 'test';
}
}
$x = new X();
$x->test();
echo $x->y; // test
You can also do this without being in the class, so if I wanted to add another property, I could just do the following:
class X {
public function test()
{
$this->y = 'test';
}
}
$x = new X();
$x->test();
echo $x->y; // test
$x->z = 'blah';
echo $x->z; // blah
Remember, when a class in instantized it is just an object which can be manipulated as any other object.
Note: If I don't call test() in the above code, it will result it an error (undefined property) because the variable has not been defined except in the test() function.
Live Example
Repl
This is perfectly normal, you can dynamicaly assign every propteries to a PHP class without error. Event if you should declare it properly to keep track of your object structure.
If you want it to throw an error, you can use the __get magic function and ReflectionClass to determine wich property is setted and wich you can't set even if I didn't see any advantage of doing this.

How to invoke a PHP closure which is assigned to a class variable?

When a PHP closure is assigned to a static class variable, then later executed, such as:
self::$FOO = function($a) {return $a;};
self::$FOO(123)
PHP warns that "the function name must be a string".
If the class variable is first assigned to an ordinary variable then executed:
$bar = self::$FOO;
$bar(123);
then all is ok.
Is there a way to execute the closure using the class variable directly, without first assigning it to an ordinary variable?
You need to use __invoke() to call a closure with the $foo() syntax.
A simple example:
class MyClass {
public static $closure;
function myFunction() {
self::$closure = function($a) { echo $a; };
self::$closure->__invoke(123);
}
}
$class = new MyClass;
$class->myFunction();
This will print out 123 :)

static method vs non-static method

Below are the examples of php class code that is static method and non static method.
Example 1:
class A{
//None Static method
function foo(){
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")<br>";
} else {
echo "\$this is not defined.<br>";
}
}
}
$a = new A();
$a->foo();
A::foo();
//result
$this is defined (A)
$this is not defined.
Example 2:
class A{
//Static Method
static function foo(){
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")<br>\n";
} else {
echo "\$this is not defined.<br>\n";
}
}
}
$a = new A();
$a->foo();
A::foo();
//result
$this is not defined.
$this is not defined.
I am trying to figure out what is the difference between these two Classes.
As we can see on the result of the none static method, the "$this" was defined.
But on the other hand the result on the static method was not defined even they were both instantiated.
I am wondering why they have different result since they were both instantiated?
Could you please enlighten me on what is happening on these codes.
Before we go any further: Please, get into the habbit of always specifying the visibility/accessibility of your object's properties and methods. Instead of writing
function foo()
{//php 4 style method
}
Write:
public function foo()
{
//this'll be public
}
protected function bar()
{
//protected, if this class is extended, I'm free to use this method
}
private function foobar()
{
//only for inner workings of this object
}
first off, your first example A::foo will trigger a notice (calling a non-static method statically always does this).
Secondly, in the second example, when calling A::foo(), PHP doesn't create an on-the-fly instance, nor does it call the method in the context of an instance when you called $a->foo() (which will also issue a notice BTW). Statics are, essentially, global functions because, internally, PHP objects are nothing more than a C struct, and methods are just functions that have a pointer to that struct. At least, that's the gist of it, more details here
The main difference (and if used properly benefit) of a static property or method is, that they're shared over all instances and accessible globaly:
class Foo
{
private static $bar = null;
public function __construct($val = 1)
{
self::$bar = $val;
}
public function getBar()
{
return self::$bar;
}
}
$foo = new Foo(123);
$foo->getBar();//returns 123
$bar = new Foo('new value for static');
$foo->getBar();//returns 'new value for static'
As you can see, the static property $bar can't be set on each instance, if its value is changed, the change applies for all instances.
If $bar were public, you wouldn't even need an instance to change the property everywhere:
class Bar
{
public $nonStatic = null;
public static $bar = null;
public function __construct($val = 1)
{
$this->nonStatic = $val;
}
}
$foo = new Bar(123);
$bar = new Bar('foo');
echo $foo->nonStatic, ' != ', $bar->nonStatic;//echoes "123 != foo"
Bar::$bar = 'And the static?';
echo $foo::$bar,' === ', $bar::$bar;// echoes 'And the static? === And the static?'
Look into the factory pattern, and (purely informative) also peek at the Singleton pattern. As far as the Singleton pattern goes: Also google why not to use it. IoC, DI, SOLID are acronyms you'll soon encounter. Read about what they mean and figure out why they're (each in their own way) prime reasons to not go for Singletons
Sometimes you need to use a method but you don't want call class, because some functions in a class called automatically like as __construct, __destruct,... (Magic Methods).
called class:
$a = new A();
$a->foo();
Don't called class: (just ran foo() function)
A::foo();
http://php.net/manual/en/language.oop5.magic.php

Closures don't work as class methods?

Am I missing something or do closures simply not work as class methods? Take this for instance:
$foo = new stdClass();
$foo->bar = function() {
echo '###';
};
$foo->bar();
Seems to give me an error of "Fatal error: Call to undefined method stdClass::bar() in /blah/blah.php on line X"
Shouldn't this instead invoke the closure that was placed in the "bar" property?
Yes, that is indeed correct.
The only way to call bar is:
$bar = $foo->bar;
$bar();
Sad, but true.
Also worth noting, because of this same effect, there is no $this inside $bar call (unless you pass it as function argument named as $this).
Edit: As nikic pointed out, the value of $this inside the closure is the same value of the scope of when the closure was created.
This may mean that $this might be undefined on two occasions: when the scope was the global PHP scope or when the scope was from a static context. This, however, means that you can in theory feed the correct instance:
class Foo {
public $prop = 'hi';
function test(){
$this->bar = function(){
echo $this->prop;
}
$bar = $this->bar;
$bar();
}
}
$foo = new Foo();
$foo->test();
Also, it seems that with some class magic, you can achieve $this->bar() as well:
class Foo {
// ... other stuff from above ...
public function __call($name, $args){
$closure = $this->$name;
call_user_func_array( $closure, $args ); // *
}
}
[*] Beware that call_user_func_array is very slow.
Oh, and this is strictly for PHP 5.4 only. Before that, there's no $this in closures :)
Also, you can see it in action here.
Methods and fields are completely separate; in fact, you can even have a method and field of the same name:
<?php
class foo{
function bar() { echo "hello\n"; }
}
$object = new foo;
$object->bar = 1;
$object->bar(); // echoes "hello"
?>
This explains why your syntax could not have created a "real" method.

PHP: extend existing class

Is it possible to set the parent of the class? I.e. an instance of the parent class gets instantiated during runtime and then a child class instance extending a certain parent instance gets created. For example:
class A {
var $test = 'default';
}
class B extends A {
public function __contsruct(A $a) {
parent = $a; //does not work
}
}
$a = new A();
$a->test = 'changed';
$b = new B($a);
$b->test == 'changed'; //should be true
I know that I could just $b = new B(); $b->test = 'changed', but that's not what I'm asking about.
A simple way to accomplish this is like so:
class Foo
{
function __construct() {
$this->hello = "Hello";
}
public function sayHello() {
echo $this->hello."!";
}
}
class Bar
{
function __construct(&$foo) {
$this->foo = $foo;
}
public function sayHelloAgain() {
echo $this->foo->sayHello()." Again!";
}
}
$foo = new Foo();
echo $foo->sayHello(); //outputs "Hello!"
$bar = new Bar($foo);
echo $bar->sayHelloAgain(); //outputs "Hello! Again!"
What you've asked for is not possible in base PHP. There are a few ways to do similar things.
You could use the highly experimental runkit extension. The runkit_class_emancipate and runkit_class_adopt functions should work. However, they operate on entire classes, not instances of a class. This probably limits their usefulness for your application.
If you're trying to emulate the expandable class features of other languages, like Ruby and Perl, runkit_method_add and related functions might be more suitable. Again, however, it still operates on entire classes.
The normally accepted "PHP way" to do things like this is via __call. In fact, with anonymous functions in 5.3, you can do something like...
class Bar {
public function say($thing) {
echo "Bar::say says: $thing\n";
}
}
class Foo extends Bar {
private $extensions = array();
public function addExtension($func_name, $func) {
$this->extensions[ $func_name ] = $func;
}
public function __call($func_name, $arguments) {
array_unshift($arguments, $this);
if(array_key_exists($func_name, $this->extensions))
call_user_func_array($this->extensions[ $func_name ], $arguments);
}
}
$f = new Foo();
$anon = function($obj, $string){ $obj->say($string); };
$f->addExtension('example', $anon);
$f->example("Hello, world!");
You'll note in __call and that in creating the anonymous function that the first argument becomes the instance. That's because PHP 5.3's implementation of anonymous functions can't reference $this. That also means that they can't reference protected or private members of the class. This can be corrected by cloning the instance and using Reflection to expose the protected and private members. See this comment on the anonymous function manual page for an example implementation.
Because of limitations of PHP, you can't directly assign an anonymous function to a property and call it. This example will not work:
class WillNotWork {
public $fatal_error;
}
$broken = new WillNotWork();
$anon = function($arg) { echo "I'm about to create a {$arg} error!"; };
$broken->fatal_error = $anon;
$broken->fatal_error("fatal");
// Fatal error: Call to undefined method WillNotWork::fatal_error()
No, because $a is a separate instance than $b.

Categories