Later static binding in PHP

Lecture



Beginning with PHP 5.3.0, a feature appeared, later called static binding, which can be used to get a reference to the called class in the context of static inheritance.

More precisely, late static binding preserves the name of the class specified in the last "non-redirected call". In the case of static calls, this is the explicitly specified class (usually to the left of the operator ::) ; in the case of non-static calls, this is the class of the object. A "redirected call" is a static call that starts with self :: , parent :: , static :: , or, if you move up the class hierarchy, forward_static_call (). The get_called_class () function can be used to get a string with the name of the called class, and static :: represents its scope.

The name "late static binding" itself reflects the internal realization of this feature. "Late binding" reflects the fact that calls through static :: will not be evaluated with respect to the class in which the called method is defined, but will be calculated based on the information in the course of execution. Also, this feature was called "static binding" because it can be used (but not necessarily) in static methods.

Restrictions self ::

Static references to the current class, like self :: or __CLASS__ , are calculated using the class to which they belong, as to the one in which they were defined.

Example # 1 Using self ::

class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}

class B extends A {
public static function who() {
echo __CLASS__;
}
}

B::test();
?>

The result of this example:

  A

Using late static binding ¶

Later, static binding attempts to remove this restriction by providing a keyword that refers to a class that was called directly during execution. Simply put, a keyword that allows you to refer to B from test () in the previous example. It was decided not to enter a new keyword, but to use static , which is already reserved.

Example # 2 Simple use of static ::

class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // Здесь действует позднее статическое связывание
}
}

class B extends A {
public static function who() {
echo __CLASS__;
}
}

B::test();
?>

The result of this example:

  B

Comment:

In a non-static context, the class called will be the one to which the object instance belongs. Since $ this-> will attempt to call private methods from the same scope, using static :: may give different results. Another difference is that static :: can only refer to static fields of a class.

Example # 3 Using static :: in a non-static context

class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo();
}
}

class B extends A {
/* foo() будет скопирован в В, следовательно его область действия по прежнему А,
и вызов будет успешен*/
}

class C extends A {
private function foo() {
/* исходный метод заменен; область действия нового метода С */
}
}

$b = new B();
$b->test();
$c = new C();
$c->test(); //не верно
?>

The result of this example:

  success!
 success!
 success!


 Fatal error: Call to private method C :: foo () from context 'A' in /tmp/test.php on line 9

Comment:

The resolving area of ​​late static binding will be fixed by the static call that calculates it. On the other hand, static calls using directives such as parent :: or self :: redirect call information.

Example # 4 Redirected and non-redirected calls

class A {
public static function foo() {
static::who();
}

public static function who() {
echo __CLASS__."\n";
}
}

class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}

public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}

C::test();
?>

The result of this example:

  A
 C
 C 


Later Static Linking in PHP (Part I)

PHP *
Later static binding in PHP Later Late Static Binding (LSB) has been a hot topic of discussion for the last three years in PHP developer circles (and finally we got it in PHP 5.3). But why is it needed? In this article, it will be considered just how later static binding can greatly simplify your code.

At a meeting of PHP developers, which was held in Paris in November 2005, the topic of late static binding was officially discussed by the main development team. They agreed to implement it, along with many other topics that were on the agenda. Details should have been agreed in open discussions.

Two years have passed since static binding was announced as the coming chip. Finally, LSB is available for use in PHP 5.3. But this event passed unnoticed by developers using PHP, from the notes there is only a page in the manual.

In short, the new late static binding functionality allows objects to also inherit methods from parent classes, but in addition allows inherited methods to have access to static constants, methods, and properties of the descendant class, and not just the parent class. Let's look at an example:
  class Beer {
     const NAME = 'Beer!';
       public function getName () {
           return self :: NAME;
     }
 }
 class Ale extends Beer {
	 const NAME = 'Ale!';
 }

 $ beerDrink = new Beer;
 $ aleDrink = new Ale;

 echo "Beer is:".  $ beerDrink-> getName (). "\ n";
 echo "Ale is:".  $ aleDrink-> getName (). "\ n";


This code will produce the following result:
  Beer is: Beer!
 Ale is: Beer!


The Ale class inherited the getName () method, but at the same time self still points to the class in which it is used (in this case, this is the Beer class). This remained in PHP 5.3, but the word static was added. Again, consider an example:
  class Beer {
   const NAME = 'Beer!';
   public function getName () {
	   return self :: NAME;
   }
   public function getStaticName () {
	   return static :: NAME;
   }
 }

 class Ale extends Beer {
   const NAME = 'Ale!';
 }

 $ beerDrink = new Beer;

 $ aleDrink = new Ale;

 echo "Beer is:".  $ beerDrink-> getName (). "\ n";
 echo "Ale is:".  $ aleDrink-> getName (). "\ n";

 echo "Beer is actually:".  $ beerDrink-> getStaticName (). "\ n";
 echo "Ale is actually:".  $ aleDrink-> getStaticName (). "\ n";


The new static keyword indicates that you need to use the constant of the inherited class, instead of the constant that was defined in the class where the getStaticName () method is declared. The word static was added to implement the new functionality, and for backward compatibility, self works just like in previous versions of PHP.

Internally, the main difference (and, in fact, the reason why the binding was called late) between these two access methods, is that PHP will determine the value for self :: NAME during the “compilation” (when PHP symbols are converted into native code that will be processed with the Zend engine), and for static :: NAME, the value will be determined at launch time (at the moment when the machine code will be executed in the Zend engine).

This is another tool for PHP developers. In the second part we consider how it can be used for good.

Later Static Linking in PHP (Part II: Practice)

PHP *
Later static binding in PHP Read the first part here.

Now we will start practice. The most telling example of using LSB, in my opinion, is when you have a set of classes that perform similar actions. In terms of web development, we often encounter such tasks when accessing database tables, especially in ORM systems. All your objects for working with tables will be similar in essence, but they will have their own functionality (and, accordingly, their own subclasses).

Suppose we have a class in the system called Storable, which implements (you guessed it) (note the translator: I did not guess :)) the storable pattern. We define classes that inherit from the Storable class and set the names of the tables in the constructors. Here's what it looks like:
  class ArticleEntry extends Storable {
	   public function __construct ($ id = null) {
		   if (! is_null ($ id)) {
			   $ id = array ('id' => $ id);
		   }
		   parent :: __ construct ('articleEntry', '*', $ id);
	   }
   }
   
   // output the text of the entry:
   $ entry = new ArticleEntry (10);  // Fetching an entry from the articleEntry table for which id = 10;
   echo $ entry-> html ('articleBody');  // output of the body of the loaded entry
   
   // update the record:
   $ entry ['ts'] = time ();  // set the time to NOW
   $ entry-> save ();  // Update Record


You can skip the details of the constructor, it is given only so that you can imagine how the class works. As you have already understood, this will save us some time and will allow us not to waste it on such trifles as simple SELECT, INSERT and UPDATE queries.

In our system, in addition to the main Article table (ArticleEntry), several more tables will be used that contain meta-data (for many-to-one), for example: tags, attachments. I also need a simple way to remove data from sub-tables before updating the data in the main table (it is easier to delete the meta data and recreate it, rather than worry about data synchronization). So, in order to model the code that is closest to the database schema, I stopped at this implementation:
  abstract class ArticleEntryAbstract extends Storable {
	   public function __construct ($ table, $ id = null) {
		   if (! is_null ($ id)) {
			   $ id = array ('id' => $ id);
		   }
		   parent :: __ construct ($ table, '*', $ id);
	   }

	   public function purge () {
		   $ s = $ this-> db-> prepare ('DELETE FROM'. $ this-> tableName. 'WHERE pulseEntryId =?');
		   $ s-> execute (array ($ this-> data ['entryId']));
	   }
   }


The focus is on the purge () method. The $ this-> tableName property is a protected property that we obtained from the Storable class. Here is an example of use:
  class ArticleEntryAttachment extends ArticleEntryAbstract {
	   public function __construct ($ id = null) {
		   parent :: __ construct ('articleEntryAttachment', $ id);
	   }
   }


I have a bunch of small classes for working with meta data tables. Unfortunately, since our system uses PHP version 5.2, I could not use the LSB functionality described in the first part of the article. And in order to delete the meta data, I have to write:
  $ attach = new ArticleEntryAttachment (10);  // SELECTS from the articleEntryAttachment table WHERE entryId = 10
   $ attach-> purge ();


If you look above how the purge () method is defined, you will see that it gets the tableName from the Storable class, which gets it from the constructors of the descendant classes. In addition to these trivial (but absolutely necessary) data, as well as getting the database object in $ this-> db, we have not achieved anything by creating an object of the class of articleentrytachment. The code would be much clearer and cleaner (and, of course, more efficient) if it were possible to call the purge () method statically. Consider this code:
  abstract class ArticleEntryAbstract extends Storable {
	   public function __construct ($ table, $ id = null) {
		   if (! is_null ($ id)) {
			   $ id = array ('id' => $ id);
		   }
		   parent :: __ construct (static :: TABLE_NAME, '*', $ id);
	   }
   
	   static function purge ($ entryId) {
		   $ db = Db :: get ();  // get the singleton database
		   $ s = $ db-> prepare ('DELETE FROM'. static :: TABLE_NAME. 'WHERE pulseEntryId =?');
		   $ s-> execute (array ($ entryId));
	   }
   }
   
   class ArticleEntryAttachment extends ArticleEntryAbstract {
	   const TABLE_NAME = 'articleAttachment';
   }


The first thing I hope you noticed is that ArticleEntryAttachment has become much easier. Now there is no need to override the constructor for subclasses, because the constructor of the parent class is self-sufficient. And now you can use the purge () method (using LSB):
  ArticleEntryAttachment :: purge (10);


Since now the purge () can get the name of the table, which is determined at the time of execution, we can make it static. As a result, the code is cleaner, execution is more efficient, support (for example, adding new subclasses) is trivial, because redundancy has been completely removed. Thanks to the PHP developers for making this possible!

The manual also discusses other ways to use LSB, including using the constant __CLASS__, so be sure to visit php.net

Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Object oriented programming

Terms: Object oriented programming