Selfless static php p. PHP Static Methods

Mar 23, 2010 dec5e

PHP 5.3 introduced an interesting feature called late static binding. What follows is a slightly free translation of the description from the official manual.

Since PHP 5.3.0, the language has introduced a feature called late static binding, which can be used to reference a callable class in the context of static inheritance.

This feature was called "late static binding". "Late binding" means that static:: will not be resolved relative to the class where the method is defined, but will be evaluated at runtime. "Static binding" means that it can be used in calls to static methods (but is not limited to them).

Limitations self::

Example #1: Using self::

The example will output:

Using Late Static Binding

Later static binding attempts to solve this limitation by introducing a keyword that references the class originally called at runtime. That is, a keyword that will allow B to be referenced from test() in the previous example. It was decided not to introduce a new word, but to use the already reserved static .

Example #2: Simple use of static::

The example will output:

Note: static:: does not work like $this for static methods! $this-> follows inheritance rules, but static:: does not. This distinction is clarified below.

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

test(); ?>

The example will output:

Note: Late static binding stops the call resolution process. Static calls using the parent:: or self:: keywords pass on the call information.

Example #4: Transferring and not transferring calls

The example will output

Edge Cases

There are many in various ways call a method in PHP, such as callbacks or magic methods. Because late static binding is resolved at runtime, this can lead to unexpected results in so-called edge cases.

Example #5 Late static binding in magic methods

foo; ?>

Reg.ru: domains and hosting

The largest registrar and hosting provider in Russia.

More than 2 million domain names in service.

Promotion, domain mail, business solutions.

More than 700 thousand customers around the world have already made their choice.

*Mouse over to pause scrolling.

Back forward

Static methods and properties in PHP

In previous materials, we have mastered the basic capabilities of object-oriented programming in PHP and are now moving on to studying more complex and interesting aspects.

Before this we always worked with objects. We've described classes as templates that create objects, and objects as active components whose methods we call and whose properties we access.

This led to the conclusion that in object-oriented programming, the real work is done using class instances. And classes are ultimately just templates for creating objects.

But in reality it's not that simple. We can access both methods and properties in the context of a class rather than an object. Such methods and properties are called "static" and must be declared using the keyword static.

Class StaticExample ( static public $aNum = 0; static public function sayHello() ( print "Hello!"; ) )

Static methods are functions used in the context of a class. They themselves cannot access any regular class properties, because such properties belong to objects.

However, among static methods, as you probably already guessed, you can access static properties. And if you change a static property, then all instances of that class will be able to access the new value.

Since a static element is accessed through a class and not through an object instance, we don't need a variable that references the object. Instead, the class name is used followed by two colons "::".

Print StaticExample::$aNum; StaticExample::sayHello();

You should already be familiar with this syntax from the basics of OOP in PHP. We used the "::" construction in combination with the keyword parent in order to access the overridden method of the parent class.

Now, as then, we refer to the class, not the data contained in the object. You can use the keyword in the class code parent in order to access the superclass without using the class name.

To access a static method or property from the same class (and not from a child class), we will use the keyword self.

Keyword self is used to access the current class, and the pseudo variable $this- to the current object. Therefore from outside the class StaticExample we access the property $aNum using its class name.

StaticExample::$aNum;

And inside the class StaticExample you can use keyword self.

Class StaticExample ( static public $aNum = 0; static public function sayHello() ( self::$aNum++; print "Hello! (" . self::$aNum . ")\n"; ) )

Note: Except when accessing an overridden method of a parent class, the "::" construct should always be used only to access static methods or properties.

By definition, static methods are not called in the context of an object. For this reason, static methods and properties are often called class variables and properties. As a consequence, you cannot use a pseudo variable $this inside a static method.

Why use a static method or property at all?

Now we have come to the most important question. The fact is that static elements have a number of useful characteristics.

Firstly, they are accessible from anywhere in the script (provided you have access to the class). This means that you can access functions without passing an instance of the class from one object to another or, worse, storing the instance of the object in a global variable.

Secondly, a static property is available to every instance of an object of that class. Therefore, you can define values ​​that should be available to all objects of a given type.

And finally Thirdly, the very fact that you don't need to have an instance of a class to access its static property or method will avoid creating instances of objects solely for the sake of calling a simple function.

To demonstrate this, let's create a static method for the class ShopProduct which will automatically instantiate objects ShopProduct. Let's define a table using SQLite products in the following way:

CREATE TABLE products (id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, firstname TEXT, mainname TEXT, title TEXT, price float, numpages int, playlength int, discount int)

Now let's create a method getInstance(), which is passed a string identifier and an object of type PDO. They will be used to retrieve a row from a database table, based on which an object of type ShopProduct, returned to the calling program.

We can add these methods to the class ShopProduct, which was created for us in earlier materials. As you probably know, PDO stands for PHP Data Object PHP data). The PDO class provides a generic interface for various database applications.

// Class ShopProduct private $id = 0; public function setID($id) ( $this->id = $id; ) // ... public static function getInstance($id, PDO $pdo) ( $stmt = $pdo->prepare("select * from products where id=?"); $result = $stmt->execute(array($id)); $row = $stmt->fetch(); if (empty($row)) ( return null; ) if ($ row["type"] == "book") ( $product = new BookProduct($row["title"], $row["firstname"], $row["mainname"], $row["price"] , $row["numpages"]); ) else if ($row["type"] == "cd") ( $product = new CdProduct($row["title"], $row["firstname"], $row["mainname"], $row["price"], $row["playlength"]); ) else ( $product = new ShopProduct($row["title"], $row["firstname"], $row["mainname"], $row["price"]); ) $product->setId($row["id"]); $product->setDiscount($row["discount"]); return $ product; ) // ...

As you can see, the method getInstance() returns an object of type ShopProduct, and it is “smart” enough to, based on the field value type create an object with the required characteristics.

I specifically omitted the error handling code to make the example as concise as possible. For example, in a real-life running version of this code, we should not be too trusting and assume that the passed PDO object was correctly initialized and connected to the required database.

In fact, we should probably wrap the PDO object in a class that guarantees this behavior. We will return to this issue in one of our future materials.

Method getInstance() more useful in the context of a class than in the context of an object. It allows us to easily convert data located in the database into an object, and for this we do not need to have a separate instance of an object of type ShopProduct.

This method does not use any methods or properties that require a separate instance of the object, so there is no reason not to declare it static. Then, having a correct PDO object, we can call this method from anywhere in the application.

$dsn = "sqlite://home/bob/projects/products.db"; $pdo = new PDO ($dsn, null, null); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $obj = ShopProduct::getInstance(1, $pdo);

Such methods work like "factories" in that they take "raw" materials (for example, data obtained from a database string or configuration file) and use them to create objects.

The term "factory" refers to the code used to create instances of objects. We will meet with you later on with examples of such “factories”.


Permanent properties

Some properties should not be changed. For example, elements such as error codes or program status codes are usually specified manually in classes. Although they should be public and static, client code should not be able to change them.

To do this, you can define persistent properties within the class. Like global constants, class constants cannot be changed after they have been defined. A persistent property is declared using a keyword const.

Unlike regular properties, a permanent property name is not preceded by a dollar sign. By convention, they are often given names consisting of all capital letters, as in the following example:

Class ShopProduct ( const AVAILABLE = 0; const OUT_OF_STOCK = 1; // ...

Persistent properties can only contain values ​​that are specific to the primitive type. A constant cannot be assigned an object.

Like static properties, persistent properties are accessed through the class rather than through the object instance. Just as a constant is defined without a dollar sign, it also does not require any leading symbol when accessing it.

Print ShopProduct::AVAILABLE;

Trying to assign a value to a constant after it has been declared will result in an error during the parse phase.

Constants should be used when a property must be accessible to all instances of a class and when the value of the property must be fixed and immutable.

This concludes this article, and in the next one we will talk about.

Did you like the material and want to thank me?
Just share with your friends and colleagues!


  • Tutorial

It's no secret that people like to ask tricky questions during interviews. Not always adequate, not always related to reality, but the fact remains a fact - they ask. Of course, the question is different, and sometimes a question that at first glance seems stupid to you is actually aimed at testing how well you know the language in which you are writing.

Let’s try to take apart one of these questions “piece by piece” - What does the word “static” mean in PHP and why is it used?

The static keyword has three different meanings in PHP. Let's look at them in chronological order, as they appeared in the language.

The first value is a static local variable

function foo() ( $a = 0; echo $a; $a = $a + 1; ) foo(); // 0 foo(); // 0 foo(); // 0

In PHP, variables are local. This means that a variable defined and given a value within a function (method) exists only during the execution of that function (method). When the method exits, the local variable is destroyed, and when it re-enters, it is created anew. In the code above, such a local variable is the $a variable - it exists only inside the foo() function and is created anew each time this function is called. Incrementing a variable in this code is meaningless, since on the very next line of code the function will finish its work and the value of the variable will be lost. No matter how many times we call the foo() function, it will always output 0...

However, everything changes if we put the static keyword before the assignment:

Function foo() ( static $a = 0; echo $a; $a = $a + 1; ) foo(); // 0 foo(); // 1 foo(); // 2

The static keyword, written before assigning a value to a local variable, has the following effects:

  1. The assignment is performed only once, on the first call to the function
  2. The value of a variable marked in this way is saved after the function ends.
  3. On subsequent calls to the function, instead of assignment, the variable receives the previously stored value
This use of the word static is called static local variable.
Underwater rocks static variables
Of course, as always in PHP, there are some pitfalls.

The first stone is that only constants or constant expressions can be assigned to a static variable. Here's the code:
static $a = bar();
will inevitably lead to a parser error. Fortunately, starting with version 5.6, it has become possible to assign not only constants, but also constant expressions (for example, “1+2” or “”), that is, expressions that do not depend on other code and can be calculated at the compilation stage

The second stone is that methods exist in a single copy.
Here everything is a little more complicated. To understand the essence, here is the code:
class A ( public function foo() ( static $x = 0; echo ++$x; ) ) $a1 = new A; $a2 = new A; $a1->foo(); // 1 $a2->foo(); // 2 $a1->foo(); // 3 $a2->foo(); // 4
Contrary to the intuitive expectation “different objects - different methods,” we clearly see in this example that dynamic methods in PHP “do not multiply.” Even if we have a hundred objects of this class, the method will exist in only one instance; it’s just that a different $this will be thrown into it with each call.

This behavior can be unexpected for a developer who is not prepared for it and can be a source of errors. It should be noted that class (and method) inheritance leads to the creation of a new method:

Class A ( public function foo() ( static $x = 0; echo ++$x; ) ) class B extends A ( ) $a1 = new A; $b1 = new B; $a1->foo(); // 1 $b1->foo(); // 1 $a1->foo(); // 2 $b1->foo(); // 2

Conclusion: Dynamic methods in PHP exist in the context of classes, not objects. And only in runtime the substitution “$this = current_object” occurs

The second meaning is static properties and methods of classes

In the PHP object model, it is possible to set properties and methods not only for objects - instances of a class, but also for the class as a whole. The static keyword is also used for this:

Class A ( public static $x = "foo"; public static function test() ( return 42; ) ) echo A::$x; // "foo" echo A::test(); // 42
To access such properties and methods, double-colon (“Paamayim Nekudotayim”) constructs are used, such as CLASS_NAME::$Variablename and CLASS_NAME::Methodname().

It goes without saying that static properties and static methods have their own characteristics and pitfalls that you need to know.

The first feature is banal - there is no $this. Actually, this stems from the very definition of a static method - since it is associated with a class, not an object, the $this pseudo-variable, which points to the current object in dynamic methods, is not available. Which is completely logical.

However, you need to know that, unlike other languages, PHP does not detect the situation “$this is written in a static method” at the parsing or compilation stage. An error like this can only occur at runtime if you try to execute code with $this inside a static method.

Code like this:
class A ( public $id = 42; static public function foo() ( echo $this->id; ) )
will not cause any errors, as long as you do not try to use the foo() method inappropriately:
$a = new A; $a->foo(); (and immediately get “Fatal error: Using $this when not in object context”)

The second feature is that static is not an axiom!
class A ( static public function foo() ( echo 42; ) ) $a = new A; $a->foo();
That's it, yes. A static method, if it does not contain $this in the code, can be called in a dynamic context, like an object method. This is not a bug in PHP.

The reverse is not entirely true:
class A ( public function foo() ( echo 42; ) ) A::foo();
A dynamic method that does not use $this can be executed in a static context. However, you will receive a warning "Non-static method A::foo() should not be called statically" at level E_STRICT. It's up to you to decide whether to strictly follow code standards or suppress warnings. The first, of course, is preferable.

And by the way, everything written above applies only to methods. Using a static property via "->" is impossible and leads to a fatal error.

The third meaning, which seems to be the most difficult - late static binding

Developers PHP language didn’t stop at two meanings of the keyword “static” and in version 5.3 they added another “feature” of the language, which is implemented with the same word! It's called "late static binding" or LSB (Late Static Binding).

The easiest way to understand the essence of LSB is with simple examples:

Class Model ( public static $table = "table"; public static function getTable() ( return self::$table; ) ) echo Model::getTable(); // "table"
The self keyword in PHP always means “the name of the class where this word is written.” In this case, self is replaced with the Model class, and self::$table with Model::$table.
This language feature is called "early static binding". Why early? Because the binding of self and a specific class name does not occur in runtime, but at earlier stages - parsing and compiling the code. Well, “static” - because we are talking about static properties and methods.

Let's change our code a little:

Class Model ( public static $table = "table"; public static function getTable() ( return self::$table; ) ) class User extends Model ( public static $table = "users"; ) echo User::getTable() ; // "table"

Now you understand why PHP behaves unintuitively in this situation. self was associated with the Model class when nothing was known about the User class, and therefore points to Model.

What should I do?

To solve this dilemma, a “late” binding mechanism was invented at the runtime stage. It works very simply - just write “static” instead of the word “self” and the connection will be established with the class that calls this code, and not with the one where it is written:
class Model ( public static $table = "table"; public static function getTable() ( return static::$table; ) ) class User extends Model ( public static $table = "users"; ) echo User::getTable() ; // "users"

This is the mysterious “late static binding”.

It should be noted that for greater convenience, in PHP, in addition to the word “static”, there is also a special function get_called_class(), which will tell you in the context of which class your code is currently working.

Happy interviews!



There are two types of PHP developers in the world. Some prefer static methods because they are easy to work with, while others, on the contrary, consider static methods to be evil and do not use them in their practice.
In this article I will try, using my experience working with several frameworks, to explain why some developers ignore best practices and use a whole bunch of static methods.

Who loves static methods?

They are especially often used by developers who have ever used the CodeIgniter framework in their work.

Also, the majority of Kohana and Laravel developers are followers of statistical methods.
Here we cannot help but mention the fact that programmers who decide to start writing their own things usually refuse to use CodeIgniter.

Why, you ask?

CodeIgniter supported PHP 4 before static methods were added in PHP 5. Additionally, CodeIgniter uses a "super object" that gives equal access to all classes assigned to the controller. Thus, they become available for use throughout the system.

This means that classes can be accessed from any model using the __get() method, which will look up the requested property using get_instance()->($var). Previously, when the __get() function was not supported in PHP 4, this was done using a foreach construct through the CI_Controller parameters and then assigning them to the $this variable in the model.

In the library you must call get_instance. The library does not force class inheritance, so there is no way to bypass the __get() function.

Volume…

This results in a rather cumbersome structure for accessing the code. The exact same functionality can be achieved with a static method without any extra effort.

And there is no particular point in discussing such a design. Okay, you can access the session data in your model. But why would you do this?

"Solution"

The Kohana developers were the first to seriously work on static methods. They made the following changes:
//was $this->input->get("foo"); // became Input::get("foo");
For many CodeIgniter developers with legacy PHP 4 who moved to the Kohana framework to take advantage of all the benefits of PHP 5, this is nothing unusual. But the fewer characters, the better, right?

So what's the problem?

Many PHP developers (especially those who are well versed in Symfony and Zend) will say: “It’s obvious - use Dependency Injection!” But not many developers in the CodeIgniter community have real experience with this process, as it is quite complex.

Another fact about the Fuel PHP framework is that so far mostly static methods act as an interface. For example, logic still has problems with statics, especially when the HMVC concept is involved.

This is pseudocode that I haven't used in FuelPHP since version 1.1:
class ControllerA extends Controller ( public function action_foo() ( echo Input::get("param"); ​​) )
Pretty standard code. This method will output the value ?bar= in the method.

What happens when we make an HMVC request to this method?
class ControllerB extends Controller ( public function action_baz() ( echo Input::get("param"); ​​echo " & "; echo Request::forge("controllera/foo?param=val1")->execute(); ) )
By calling in the browser controllerb/baz, you will see the output "val1" but if you type controllerb/baz?param=override, then get both calls to get the method returning the same value.

Relevance

The global code won't give you any relation. An example is better than any words:
$this->request->input->get("param");
The requested object will contain a completely new instance for each request, then an input object will be created for each request that only contains the input data for the specific request. This is true for FuelPHP 2.0 plans to work and solves the Dependency Injection issue as well as the HMVC issues.

What about rough syntax?

Symfony or Zend developers do not suffer from this, but those who use CodeIgniter will have nightmares about “returning to PHP 4” for a long time.

$this always refers to the "current" object, and you definitely shouldn't use it to access global code.

$this->request->input->get() may look like a long form of CodeIgniter syntax, but we're actually just in a controller. When the controller instantiates a new query nested within it, the query constructor also receives an instance as input.

If you are in a model or other class, then access like $this->request->input->foo() will not work because $this is not a controller.

The Input::get("foo") construct creates a façade for logic instances in the background. But this does not solve the issues related to the operation of the global code. Those who are the laziest when testing applications can switch between the two modes without having to completely use a new framework.

There's a great video from Taylor Otwell (creator or laravel 4) in which he describes how you can replace static code with a single instance tested through his DiC container.

Laravel 4 - IoC Controller Injection & Unit Testing from UserScape on Vimeo.

This is a great presentation of how you can get away with not using static methods in Laravel. Although some modern frameworks, at first glance, are very similar to Kohana, they solve even the most standard problems in completely different ways.

On this sad note...

I'm currently in the process of converting PyroCMS from CodeIgniter to Laravel. Trying to go straight from global PHP 4 code to perfect dependency injection is absolute suicide. An intermediate step before using the CI loader is to use PHP 5, PSR-2 autoloader code with a bunch of static methods. Well, for now we are still in CodeIgniter.

The transition from static to DiC code can be easily demonstrated when we finally make the transition to Laravel.

Moving from tightly coupled CodeIgniter code to testable PSR-2 is a major challenge. The Pyro team is on its way - and it's going to be epic.

PHP has the ability to define a method as static. A static method does not have access to the properties of an object. Such methods can only be called in the context of a class, not in the context of an object.

The most important thing to understand is that static properties and methods belong to classes, not objects.

An example will immediately make it clear. Let's create a Math object (short for mathematics in English).

Static <a href="https://mapstr.ru/en/php-magicheskie-metody-get-getter-y-i-setter-y-magiya-kotoraya.html">PHP methods</a>
"; $math_1 = new Math(); $math_2 = new Math(); $math_3 = new Math(); $math_4 = new Math(); echo "Objects created: " . Math::getCount(); ?>

This class provides tools for working with mathematical functions without the need to create an object. The class has a constructor that increments the static property $count by one. The class remembers the value of this property because it is static.

By the way, to declare a method or property static, use the word static , and to access a static property, use the word self followed by a double colon "::".

All this is better understood by comparison, especially by comparing a working example with an erroneous one. Let's expand our example a little.

PHP Static Methods counter++; ) public static function calcSin($x) ( return sin($x); ) public static function calcSQRT($x) ( return sqrt($x); ) public static function getCount() ( return self::$count; ) public function getCounter() ( return $this->counter; ) ) echo Math::calcSin(1); echo "
"; echo Math::calcSQRT(9); echo "
"; $math_1 = new Math(); $math_2 = new Math(); $math_3 = new Math(); $math_4 = new Math(); echo "Objects created: " . Math::getCount(); echo "
"; echo "Objects created: " . $math_4->getCounter(); ?>

In this example, we added a regular $counter property to the class, which was also incremented by one in the constructor. But a regular property belongs to an object, so it doesn't persist between calls to objects. Whenever an instance of a class (object) is created, the property will be equal to zero; in the constructor it will be increased by one.

A static property belongs to the class, so its value is preserved.

Below are a few more examples that show how static properties and methods work.

Trying to use the $this variable in a static method will result in an error (Fatal error: Using $this when not in object context).

PHP Static Methods age. "old."; // this is a "Using $this when not in object context" error. ) ) $TestClass = new TestClass(); $TestClass->sayHello(); ?>

By the way, without the line:

$TestClass->sayHello();

There will be no error, but as soon as you try to run a static method with the $this variable in your code, you will immediately receive an error message.

If you remove the word static in this example, there will be no error.

If you access a property of an object from a static method, this will lead to an error. You can only access static properties using the self::$age construct. Please note that there is a $ sign before the variable name, unlike the $this->age construct.

PHP Static Methods sayHello(); ?>

In this example, if you remove the word static before the property name, the error "Access to undeclared static property" will appear.

There are no static properties on class objects.

PHP Static Methods "; print_r($TestClass); echo ""; ?>

A static method can be called using the self::method() construct. Example:

PHP Static Methods printHello(); ?>

A static property can be obtained in the context of a class using the syntax:

echo TestClass::$age;

Moreover, an attempt to access a regular property in this way will lead to the error: “Fatal error: Access to undeclared static property.”

A static property can be changed in the context of a class using the syntax:

TestClass::$age += 20; // For example

Another code example with static methods and properties

This example also shows simple options for using static methods and properties. The more simple code you understand, the better you will remember the material.

PHP Static Methods ".TestClass::$age; // echo TestClass::$txt; // Error: Fatal error: Access to undeclared static property. echo "
"; TestClass::sayHi(); echo "
"; TestClass::sayHello(); // And this is how I managed to access a static variable through an object ($obj::$age)... echo "
"; $obj = new TestClass; echo "Accessing a static variable through an object: " . $obj::$age; ?>

Please note, and this is important, in this example we accessed the non-static sayHi() method using the syntax for accessing static class members.

Summary

  • The main thing: static properties belong to classes, not objects.
  • From a static method you cannot access ordinary properties and methods of the class; $this->name does not work here.
  • From a static method, you can access static properties using self::$name .
  • Static properties of a class are not available to objects.
  • A normal method can access a static property using self::$name .
  • A static property can be obtained in the context of a class using the syntax: TestClass::$age .
  • A regular method can be called in the context of both an object ($object->method()) and a class using the TestClass::method() syntax.
  • Using the $object::$age syntax, I was able to access a static property through an object.

Parallels with JavaScript

JavaScript has a Math class that contains a lot of different math functions.

To perform mathematical calculations (calculating sine or exponent) in JavaScript, you do not need to create an object of the Math class, since its methods are static. Before learning PHP, I could not understand what it was, and only after studying classes and objects in PHP did everything fall into place in my head.

It's actually very convenient to have direct access to the math methods of the Math class without having to create an object.

mob_info