Object Oriented Programming (OOP) in PHP Interfaces, Classes, Abstract Classes, Objects

Lecture



Like most modern programming languages, PHP is object-oriented. What does this mean? Object-oriented programming, or OOP, is an approach in which the main elements of a program are classes, interfaces, and objects.

OOP is based on three principles called Encapsulation , Inheritance and Polymorphism . These concepts will be explained below. In the meantime, I’ll talk about basic terms:

Classes, interfaces, objects

A class is a description of some entity in a programming language. Classes encompass the following:

  • Data members, or fields. In essence, a data field is a variable declared inside a class. Data members store the state of class objects.
  • Behavior. The behavior of a class is described by member functions of the class, which in the OOP are called "methods".
  • Access rights to data fields and methods.
  • Inheritance . Each class can be a descendant of another class, and in turn be an ancestor of the next class. Heir, sorry, inherits from its parent methods and data members, that is, behavior and state information.

An object is an instance of a class. You can create as many instances of the same class as you need. It is now easier to understand why a “class” is so called: a class describes a whole class of objects that may have the same behavior and properties inherent in a given class.

An interface is a description of the capabilities provided by the class implementing this interface. The interface does not describe, in contrast to a class, a specific behavior, but contains a description of methods without their body. The interface assigns the responsibility for implementing these methods to the class that declared that it implements this interface.

An interface declaration, an interface implementation, and an example of usage — all this is shown in the code snippet below. You can copy it to a PHP file and play with it. This program will display the numbers 1, 2, 3, and so on, when you press the F5 button in the browser.

 // DataStorage interface.
 interface DataStorage {
   // setData - saves the value of $ value under the name $ name.
   public function setData ($ name, $ value);

   // getData - returns the value previously saved
   // named $ name
   public function getData ($ name);
 }

 // FileDataStorage class - Implementation of the DataStorage interface,
 // working with files.
 class FileDataStorage implements DataStorage {
   private $ directory;

   // This is a constructor, that is, the method that PHP calls.
   // when creating a class instance with the new operator.
   // In the constructor, the object is initialized.
   public function __construct ($ directory) {
     $ this-> directory = $ directory;
   }

   private function getFileName ($ dataName) {
     return $ this-> directory.  "/".  $ dataName.  ".txt";
   }

   public function getData ($ name) {
     $ fname = $ this-> getFileName ($ name);
     return is_file ($ fname)?  file_get_contents ($ fname): null;
   }

   public function setData ($ name, $ value) {
     if (! is_dir ($ this-> directory)) {
       mkdir ($ this-> directory, 0777, true);
     }
     return file_put_contents ($ this-> getFileName ($ name), $ value);
   }
 }

 // function getNextNumber () - refers to the data element 'value',
 // increments it by one and returns the resulting value.
 // This function can work with any DataStorage.
 function getNextNumber (DataStorage $ storage) {
   $ value = $ storage-> getData ('value');
   $ value ++;
   $ storage-> setData ('value', $ value);
   return $ value;
 }

 // Create an instance of the FileDataStorage class
 // and pass it to the function.
 $ storage = new FileDataStorage ('data');
 echo getNextNumber ($ storage);

 comment 

Object Oriented Programming (OOP) in PHP Interfaces, Classes, Abstract Classes, Objects

 interface someInterface {public function someMethod ();  } 

 interface anotherInterface {public function someMethod ();  } 

 class Foo implements someInterface, anotherInterface 
 {
  public function someMethod () {echo 'someMethod () was called'.PHP_EOL;  }
  }

  $ foo = new Foo ();
  $ foo-> someMethod ();

 So that will display the script as a result of their work.
  . 
 The correct answer is: "Fatal error: Can not inherit abstract function ..."
  in PHP, a class cannot implement several interfaces containing the same methods, since this causes ambiguity. 

 Interfaces are used to pre-outline a class.  Here is an example, but the interface is only the first draft of a solution to a post-applied task. 

Imagine a customer question: how long will it take to implement a new part related to deposits?

Look at the code:

 interface Bank {
	 boolean depositFunds (Funds funds);
 }


All you have to do is implement the interface. How much time do you need? Hint: there is no right or wrong answer and you probably need to make some assumptions.

What is the difference between abstract class and interface ? This question is often asked at interviews when applying for a job as a programmer. Moreover, they are asked not only by the “juniors” but also by the “seniors” quite well, and the latter often “float” on this issue, trying to recall the subtleties of the implementation of the PLO in their favorite language.

For them, as well as for myself (not to forget) I will describe these differences in detail. All of the above concerns PHP, I cannot vouch for the specifics of implementation in other languages.

So:

  • In a rough approximation, the interface can be considered an extreme degree of an abstract class.
  • Interfaces, in contrast to abstract classes, support multiple inheritance. Those. a child class can implement 2 or more interfaces simultaneously: class A implements Int1, Int2, but class A extends AbstrB
  • The interface contains solely method declarations, but no implementation. The abstract class can contain both abstract methods (without implementation) and ordinary ones.
  • An interface cannot include properties; in an abstract class, as in any other, properties may be present.
  • A descendant class must implement all interface methods, while abstract class methods may not be present in it.
  • In the interface, all methods are public, in an abstract class, they may contain access specifiers (private, protected, pubic).

As we can see, the abstract class and interface have very significant differences. If I did not mention something - add me in the comments.

Also, I would like to know your opinion - when it makes sense to use an abstract class instead of an interface?

The interface is often used in the design. naprmer seigneur sets what method in general odizathen for what

and junion implements it

.fine interfaces are needed for pairing different classes, to bring something common in them or demanding something common and not changing when changing one class to another in the system

In the above example, in the class declaration, the service words public and private are found. They set the level of access to the methods and data members of the class. There are several levels of "access" to the members of the class:

  • public - any program code can access this class member.
  • protected - such member can be accessed only from the class method where this member is declared, as well as from the descendant classes that inherited this member from the parent.
  • private - available only for class methods where this member is declared. In the descendant classes of private methods and data fields will not be available.
  • by default, if no access specifier is specified, public is used.

What does this division by access levels give? Thanks to the ability to “hide” the internal data members from the external code, a better isolation of the implementation details from the class users is achieved. With a well-designed class interface, the methods through which the class interacts with the outside world will be accessible from the outside, and the internal and auxiliary methods and fields will not be available. This increases the visibility of the class interface, and often prevents gross errors.

but even declaring a protected or private method, it is still fashionable to access it, for example, through reflection or closure


PHP 5.4 gave us a new closure API and the Closure # bind () method.
Closure # bind () basically allows you to get an instance of a closure with the scope of a given class or object. Gracefully! This is how you can add API to existing objects!
Let's break the object encapsulation according to our needs.
Methods of accessing private properties have already been explained in the documentation, but I still give an example.
Steal the private property Kitchen # yummy:


Define the closure to get this field:

  yummy;
 }


Now let's steal the yummy from the Kitchen instance:


Unfortunately, we get a fatal error in $ sweetsThief:

 Fatal error: Cannot access private Kitchen :: $ yummy in [...] on line [...]


Let's make our thief smarter Closure # bind ():


Luck!

Closure :: bind vs Reflection: speed


I made a simple benchmark for 100,000 iterations of initialization:

  yummy;
     }, null, 'Kitchen');
 }

  setAccessible (true);
 }


On the newly compiled PHP 5.5 (Ubuntu 13.04 amd64 box), the first test took 0.325 seconds, and the second 0.658.

Reflection is much slower. (49%)

But this is not at all interesting, since no one will need to initialize 100,000 times the same thing, at least to me for sure. What is really interesting is access to private properties. I tested it too:

  yummy;
 }, null, 'Kitchen');

 for ($ i = 0; $ i <100000; $ i + = 1) {
     $ sweetsThief ($ kitchen);
 }

  setAccessible (true);

 for ($ i = 0; $ i <100000; $ i + = 1) {
     $ sweetsThief-> getValue ($ kitchen);
 }


The first test took ~ 0.110 seconds, and the second ~ 0.199!
This is much faster reflection, impressive! (55%)

Access to private properties via links


There is another advantage, using closures instead of reflection, we can work with the properties of an object by reference!

  yummy;
 }, null, $ kitchen);


 $ cake = & $ sweetsThief ($ kitchen);
 $ cake = 'lie';

 var_dump ('the cake is a'. $ sweetsThief ($ kitchen));

Universal Access Method


Given the above, we can write a simple wrapper to obtain any property of any object:

  $ property;
     }, $ object, $ object) -> __ invoke ();

     return $ value;
 };

 $ kitchen = new Kitchen ();
 $ cake = & $ reader ($ kitchen, 'cake');
 $ cake = 'sorry, I ate it!';

 var_dump ($ kitchen);


Working example https://3v4l.org/nCMor
We have access to any property, anywhere, and even by reference. Hooray! We broke the rules again!

Due to the possibility of restricting access to class members, a class can be considered a “black box”, providing a simple interface for communicating with the outside world, but having perhaps a complex implementation inside, but users of the class do not need to know about this implementation. This principle is called Encapsulation .

In the example above, FileDataStorage provides users with two seemingly simple methods. Inside these methods, actions occur that the client (in our example, the getNextNumber function) does not even guess. For the “services” consumer function called DataStorage, the implementation details of our FileDataStorage, which is actually passed to the function, have no meaning. The function works with the $ storage object as a black box, calling its methods and passing parameters to them, as required by the DataStorage interface. At this point, we come to the following important concept:

Polymorphism

Polymorphism, as follows from the word itself, is the ability of entities of one class to take on different forms, that is, to have different behavior. In OOP, polymorphism is provided by the possibility of class inheritance from each other, as well as the possibility of implementing interfaces.

Again we turn to our example above. In this example, the getNextNumber function gets an instance of DataStorage for its internal needs. DataStorage is an interface, that is, any implementation of this interface would fit our function, because in any DataStorage implementation, the getData and setData methods are required. But what specific implementation of the DataStorage will be transferred, the getNextNumber function has no idea, and she doesn’t need to know. In our example, there is only one implementation of DataStorage - FileDataStorage, but no one bothers to make other implementations, for example, those who can store data in a database or on a remote server. The getNextNumber function will work equally well with any of these implementations.

This property DataStorage, take a different behavior depending on the specific implementation, and is called polymorphism .

What is all this for?

In practice, the design of various functionalities in the form of classes is very convenient. A class is an isolated set of function methods and data with which these methods work. Access restriction ensures that only class methods can work with this data. All this allows you to create all kinds of "bricks", that is, components that implement this or that functionality: working with a database, working with images, processing user input and so on and so forth. If every such brick has a simple interface and If there are fewer dependencies on other building blocks, then such a component will be easily portable and it will be possible to use it in more than one project, saving time and effort.

Dependency connectivity

In our example (again!), The getNextNumber function depends only on the DataStorage interface. getNextNumber does not depend on any specific implementation of DataStorage, which allows you to use this function in any environment, you just need to create a suitable implementation of DataStorage. This is an example of weak dependency .

Connectivity (cohesion) is a concept that defines how closely the elements of a single module are interconnected. It can be considered a "module" class, and "elements" are its methods. Then the connectivity determines, in essence, how narrow (specific) the functional implements this class.

OOP allows you to provide a weak dependence (connectedness, coupling) of the program components from each other, with their high cohesion. The weak dependence of the components on each other with their high coherence is a sign of good software design, because it means that:

  • Each class performs only its specific responsibility, part of the functionality of the system. And vice versa, only one class is responsible for each element of the system functional. For example, if the system needed to change the security policy by adding a limit on the minimum password length, then the changes should be made only in one place of the program responsible for checking the password for security.
  • Classes are weakly dependent on each other: you can replace one object with another, and it will not be necessary to make changes to adjacent components of the system.

In practice, this means ease of maintenance, that is, the ease of making changes to an existing program to change existing and add new features.

Epilogue

The above can be considered a very brief introduction to OOP, and the programming language does not matter. It is enough to replace the example in the middle of the page with another language - and everything will be true for another language.

In order to make programs correctly using the principles of OOP, you need to “feel” this approach, which may not happen immediately. It often turns out that the object design of a program is one of the most difficult places in the development of large software systems. And sometimes this design changes during development! Therefore, the classes are "cubes", large and smaller - you need to see "from a distance" in order to operate with them, making up the structure of the application. Various frameworks help a lot with this, offering a partly ready-made design, on which it remains to hang up the capabilities necessary for a specific application.

There are so-called "design patterns", these are tactical methods of solving various problems of application design. Each such technique, or pattern, has a generally accepted name, which allows developers, communicating (often directly through comments in the code), to explain in a few words how the program interacts with various program components. However, design patterns are a topic for a separate article, not even for a whole book (there are actually a lot of such books).

In the future, in the course of the study, we will return to various aspects of the PLO, so that the basic knowledge will be supplemented by more detailed, as well as experience in practical use.

PHP5 changes

New access levels private and public

In PHP5, new access level modifiers for class variables have been added. As in many other programming languages, they are called private, protected and public.

Private is the most restrictive modifier. The private class variable can only be used in the class in which it is declared. It cannot be accessed from other program code.

Protected is an extension of the private area that adds the ability to access a variable from descendant classes.

Public - extends protected modifier that specifies the widest area of ​​access. The ability to use a variable in descendant classes is added to the ability to access the variable directly from another code. Strictly speaking, public is not a new area of ​​access. Previously in PHP all class variables were public variables.

Private variables are used for algorithms that are used only in the current class and cannot be redefined in descendant classes. Protected can be used when organizing a family of objects with similar algorithms and organized into a hierarchy. Using public variables is usually not a good practice, but sometimes justified. They can be used if the class has many properties that should be available to all algorithms using this class.

Similarly, private / protected / public midifiers are applied to class methods. Methods declared without a modifier are public methods.

If a method or variable is redefined in a subclass, the access level must be the same or higher. For example, a protected method in a descendant class can be made public, but it cannot be private.

For example, consider the classes NewClass and NewClass1.

class NewClass {
// new PHP5 modifiers
private $ myPrivateVar = 'myPrivateVar';
protected $ myProtectedVar = 'myProtectedVar';
public $ myPublicVar = 'myPublicVar';
// old PHP declaration
var $ myVar = 'myVar';
}

class NewClass1 extends NewClass {
function getProtectedVar () {
return $ this-> myProtectedVar;
}
}

NewClass contains several variables with different access areas. NewClass1 is used to test the scopes associated with inheritance.

Create class objects:

$ c = new NewClass ();
$ c1 = new NewClass1 ();

We
address variables: print $ c-> myPrivateVar;
Directly accessing a private variable results in an error.
print $ c-> myProtectedVar;
Directly accessing a protected variable results in an error.
print $ c-> myPublicVar;
Accessing a public variable returns its value.
print $ c-> myVar;
Reference to a variable declared in the old style is equivalent to reference to a public variable.
print $ c1-> myPrivateVar;
The private variable was not inherited by the class NewClass1. Referring to it is equivalent to referring to an undeclared variable.
print $ c1-> myProtectedVar;
Protected variable was inherited and direct access to it leads to an error. To verify that it was inherited along with the initial value, you can call "print $ c1-> getProtectedVar ();".
print $ c1-> myPublicVar;
The public variable was inherited and accessing it returns its value.

Abstract classes and methods (abstract)

Abstract classes are used to create a family of objects with a single interface. They are also used when it is necessary to prohibit the creation of an object of a certain class.

An example of creating and using an abstract class:

abstract class NewClass {
abstract function myMethod ();
}

class NewClass1 extends NewClass {
function myMethod () {
return 'myMethod';
}
}

$ c = new NewClass1 ();
print $ c-> myMethod ();

If a method is defined as abstract, it must be redefined in a descendant class. The parameters of the overridden method must match the parameters of the abstract method. The access level modifier for abstract methods is not taken into account. The access level is determined by the method that overrides the abstract.

Interfaces

The interface is similar to the abstract class, except that the use of interfaces allows the use of multiple inheritance. Thus, a class can implement several interfaces simultaneously, and not extend only one abstract class.

An example of using the interface:

interface Printable {
public function dump ();
}
interface Editable {
public function edit ();
}

class NewClass implements Printable, Editable {
function dump () {}
function edit () {}
}

$ c = new NewClass ();
print (($ c instanceof Printable)? 'true': 'false');

Classification of functions at the class level

For function parameters, you can set a class whose object can be passed by this parameter. While running the script, the design

function myFunction (MyClass $ obj) {
}

equivalent design

function myFunction ($ obj) {
if (! ($ obj instanceof MyClass || $ obj == null)) {
die ('Argument 1 must be an instance of ClassName');
}
}

At the same time, instanceof is distributed not only to the name of the class, but also to all of its ancestors and the implementable interfaces.

For example, the following code will run without errors:

interface Editable {
function edit();
}

abstract class View {
abstract function createView();
}

class NewClass extends View implements Editable {
function createView() { }
function edit() { }
function createMyView(View $obj) { }
function doEdit(Editable $obj) { }
}

$c = new NewClass();
$c->createMyView($c);
$c->doEdit($c);

Финальные классы и методы (final)

The final method cannot be overridden in a descendant class. The final class cannot be used to create successor classes. This can be useful when it is necessary to keep the algorithm encapsulated in the class unchanged. For example, to restrict a programmer using a library from redefining behavior. The use of the final classes together with the typing of the parameters of the functions creates almost 100% obstacle to the extension or replacement of functionality. Naturally, with open source, removing final from a class or method is not difficult, but, for example, final is often used for classes defined in PHP itself or its extensions (Exception class, DOM extention).

An example of the final class and the final method:

final class Security {
function createUser () {
...
}
}

class View {
final static function createView (Security $ user) {
...
}
}

Since the Security class is final, and the parameter of the View :: createView function can only be a final class object or null, this gives 100% guarantee that if an object is passed to the createView function, it will be only a Security class object, and not replaced.

Cloning objects

In PHP4, a simple $ clonedObject = $ object operation was enough to clone an object. All the properties of the $ object object were simply copied to the $ clonedObject object. It was possible to change the cloaking algorithm by writing our own method for this. In PHP5, the special name __clone was introduced for this method and the access to the created object was simplified. To refer to a new object, use $ this, to refer to an existing one (whose clone is being made), respectively, $ that.

If there is no __clone method, then the standard method will be called, copying all the properties of the object.

For example, it looks like this:

class Node {
private $ next;
private $ name;

function __clone () {
$ this-> name = $ that-> name;
$ this-> next = null;
}

function setName ($ name) {$ this-> name = $ name; }
function getName () {return $ this-> name; }
function setNext (Node $ next) {$ this-> next = $ next; }
}

$ n1 = new Node ();
$ n1-> setName ('Node1');

$ n2 = new Node ();
$ n2-> setName ('Node2');
$ n1-> setNext ($ n2);

$ n = $ n2 -> __ clone ();
print_r ($ n);

В примере рассматривается класс для создания списка, т.е. цепочки объектов, в которой каждый объект сожержит указатель на следующий. При этом можно получить клон любого объекта в цепочке, и новый объект будет "вынутым" из цепочки (не содержать ссылки на следующий объект).

Пример также демонстрирует, что к можно внутри метода __clone можно получить доступ к private переменным объектов $this и $that.

Constructors

Основным недостатком структуры конструкторов в PHP4 является необходимость синхронизации имени конструктора и имени класса. Поскольку имя конструктора должно совпадать с именем класса, то, при изменении имени класса, приходится переименовывать и конструкторы. В случае, если класс имеет несколько наследников, приходится аккуратно изменять в классах наследниках наследуемый класс (extends) и вызов конструктора класса-предка (parent).

Introducing the PHP5 constructor for a class with the common name __construct makes it easy to rename classes during their development. If the class has both __construct and a function whose name matches the class name, then __construct will be called as the constructor. When the constructor method is overloaded, the constructor of the ancestor class is called via parent :: __ construct ().

An example of using constructors:

class NewClass1 {
function __construct () {
print 'NewClass1 :: __ construct called';
}
}

class NewClass2 extends NewClass1 {
}

class NewClass3 extends newClass2 {
function __construct () {
print 'NewClass3 :: __ construct called';
parent :: __ construct ();
}
}

$ n1 = new NewClass1 ();
// output NewClass1 :: __ construct called

$ n2 = new NewClass2 ();
// output NewClass1 :: __ construct called - the constructor is inherited and called

$ n3 = new NewClass3 ();
// output NewClass3 :: __ construct called and NewClass1 :: __ construct called

In this case, if the constructor is declared with the private modifier, then a class with such a constructor cannot be created. However, parent: __ construct is possible. This provides another way to avoid creating a class, in addition to declaring it abstract.

Destructors

Destructors are new to PHP. They are very useful for doing work on freeing resources, such as closing open files or connecting to a database. For destructors, the name is definitely __destruct. As for constructors, if the destructor is inherited and not overloaded it will be called. If it is overloaded, only the overloaded constructor will be called. To call the ancestor object's destructor, use parent :: __ destruct (). The destructor is called without parameters.

Destructor usage example:

class Computer {
function compute () {
// large computation-intensive.
}

function __destruct () {
// send an email that everything is done
}
}

$ c = new Computer ();
$ c-> compute ();

Constants

В классах могут быть объявленны константы. Это является еще одним методом (вместе с final классами и методами) для повышения структурности и удобочитаемости кода.

Пример определения и использования констант:

final class ControlTypes {
const Textbox = 1;
const Label = 2;
const Listbox = 3;
const Textarea = 4;
const Link = 7;
const Button = 6;
}

class Control {
private $type;

function __construct($type) {
$this->type = $type;
}
}

$c = new Control(ControlTypes::Textbox);

К константам невозможно применять модификаторы public, protected, private. Константы всегда public. Обращаться к константам можно только через имя класса, например ControlType::Textbox. Обращения через $this или другой указатель на объект класса не поддерживаются. В константе может быть только значение примитивного типа, т.е. строка или число. Константы наследуются и могут быть переопределены в классах-потомках.

Интересной особенностью является то, что интерфейсы могут содержать константы. For example:

interface myInterface {
const test = 2;
}

Система перехвата исключений (exceptions)

Exceptions are an integral part of any modern language. The exception-capture system combines the throw statement, the "try {..} catch () [catch () ...]" language structure and the main Exception object. Unlike Java exceptions, PHP does not have a final finally block.

The main use of the exception system is to use a try / catch structure to separate the main program code and error handling blocks. The exceptions mechanism also allows you to correctly handle exceptions that occurred not directly in the executable code, but in the functions used.

The following example demonstrates the separation of code from non-standard situation handlers:

/ **
* Remarks:
* DatabaseConnection constructor can throw DatabaseException
* The getUser () method can throw UserNotFoundException
* The sendMail () method may throw a MailServiceException
* /
try {
$ cn = new DatabaseConnection ();
$ admin = cn-> getUser ('Admin');
$ admin-> sendMail ('Database check is complete');
} catch (DatabaseException $ e) {
print "Unable to create a database connection. Reason:". $ e-> getMessage ();
} catch (UserNotFoundException $ e) {
print "User does not exist";
} catch (MailServiceException $ e) {
print "Error sending letter:". $ e-> getMessage ();
} catch (Exception $ e) {
print "General error:". $ e-> getMessage ();
}

In general, using the exception system can be replaced by using if and goto structures, or only if, but the program code becomes much more cumbersome as a result.

The PHP exception system works only with exceptions thrown by the throw statement. Language syntax errors are not handled by try / catch blocks for obvious reasons.

In PHP at the moment there is only one exception class defined: Exception. For more flexible work with the message system, you can add your own exception classes, but inherit them from the base class Exception, so that you can always catch a catch.

The main methods of the Exception class are: getMessage (), getCode (), getTrace (), getFile (), getTraceAsString (), _toString (). All methods are final, except the constructor and _toString (). Thus, the additional functionality of the Exception descendant classes (sending mail with error information, writing to the log) can be implemented in the constructor.

The Exception class is declared directly in the PHP Engine, but its approximate model can be represented in this way (according to www.zend.com):

class Exception {
function __construct (string $ message = NULL, int $ code = 0) {
if (func_num_args ()) {
$ this-> message = $ message;
}
$ this-> code = $ code;
$ this-> file = __FILE__; // of throw clause
$ this-> line = __LINE__; // of throw clause
$ this-> trace = debug_backtrace ();
$ this-> string = StringFormat ($ this);
}

protected $ message = 'Unknown exception'; // exception message
protected $ code = 0; // user defined exception code
protected $ file; // source filename of exception
protected $ line; // source line of exception

private $ trace; // backtrace of exception
private $ string; // internal only !!

final function getMessage () {
return $ this-> message;
}
final function getCode () {
return $ this-> code;
}
final function getFile () {
return $ this-> file;
}
final function getTrace () {
return $ this-> trace;
}
final function getTraceAsString () {
return self :: TraceFormat ($ this);
}
function _toString () {
return $ this-> string;
}
static private function StringFormat (Exception $ exception) {
// ... a function not available in PHP scripts
// that returns all relevant information as a string
}
static private function TraceFormat (Exception $ exception) {
// ... a function not available in PHP scripts
// that returns the backtrace as a string
}
}

Using objects without reference to them

A very serious inconvenience in PHP4 was the invocation of a chain of methods. In PHP4, it is impossible to create an object without reference to it, since the objects were actually only a syntax construct and were equivalent to arrays at the kernel level. This generated, for example, the following constructions:

$ page = & $ this-> getPage ();
$ page-> registerControl ($ this);

Of course, this is not very convenient. Created at the PHP5 kernel level, the object reference table makes it unnecessary to have object references. This makes the following construction possible:

$ this-> getPage () -> registerControl ($ this);

But it should be noted that although this approach is more concise in writing, its unreasonable use is fraught with very irrational code. For example, it is highly recommended not to do this:

for ($ i = 0; $ i <100; $ i ++)
$ myObject-> getProperty ('relatedObject') -> getAncestor ($ i) -> update ();

During the operation of this code, two hundred objects and three hundred method calls are created. In a very simple way, you can reduce to creating a hundred objects and two hundred and one method calls:

$ relatedObject = $ myObject-> getProperty ('relatedObject');
for ($ i = 0; $ i <100; $ i ++)
$ relatedObject-> getAncestor ($ i) -> update ();

Despite the obviousness of this approach, quite often written code can be improved with its help.

In future versions of PHP, most likely, we can expect extensions of this approach to ordinary arrays. If they, of course, still remain - objects tend to capture more and more functionality :-). Then, perhaps, the following construct will be available: print ((new ServerEnvironment ()). GetServerVariables ()) ['REQUEST_URI'].

Initialization of class variables outside the constructor

The initial value of a class variable can now be specified directly when it is declared. However, its value can only be of a primitive type, i.e. string or number. However, this method is the only way to set the value of a static class variable. For example:

class MathUtils {
static private pi = 3.1415926;
...
}

Otherwise, it is impossible to determine pi, since for static variables there is no “static” constructor.

Class static methods

Static methods of a class can be called directly on the class, and not through its one of its objects. Accordingly, the $ this pointer is not available in static methods.

In fact, class declaration with static methods is, to a greater extent, a method of grouping functions and their common constants and variables. For example, the functions of connection with MySQL, so well known in PHP, could be formed as a MySQL class:

interface DatabaseInterface {
static function connect ($ host, $ user, $ password);
static function select_db ($ database);
static function query ($ query);
static function fetch_array ();
static function free_result ($ result);
static function close ($ link);
}

class MySQL implements DatabaseInterface {
static CLIENT_COMPRESS = 1;
static CLIENT_IGNORE_SPACE = 2;

...

static function connect ($ host, $ user, $ password) {
...
}

static function select_db ($ database) {
...
}
}

The use of this approach ensures that all classes of database access will implement one interface (substitutability), reduces the likelihood of name conflicts, simplifies the existence of several versions of the database access class, etc.

instanceof operator

The new operator "checked object instanceof checked class" allows you to check whether the checked class falls in the list of the inheritance tree of the class of which the object being checked is. For example, it looks like this:

interface Editable {
function startEdit ();
function endEdit ();
}

class Control {
function getValue () {
// ...
}
}

class EditableControl extends Control implements Editable {
function startEdit () {
// ...
}
function endEdit () {
// ...
}
}

$ c = new Control ();
$ ec = new EditableControl ();

print '$ c instanceof Editable ='. ($ c instanceof Editable? 'true': 'false'). '
';
print '$ c instanceof Control ='. ($ c instanceof Control? 'true': 'false'). '
';
print '$ c instanceof EditableControl ='. ($ c instanceof EditableControl? 'true': 'false'). '
';
print '$ ec instanceof Editable ='. ($ ec instanceof Editable? 'true': 'false'). '
';
print '$ ec instanceof Control ='. ($ ec instanceof Control? 'true': 'false'). '
';
print '$ ec instanceof EditableControl ='. ($ ec instanceof EditableControl? 'true': 'false');

The result of this code will be:

$ c instanceof Editable = false
$ c instanceof Control = true
$ c instanceof EditableControl = false
$ ec instanceof Editable = true
$ ec instanceof Control = true
$ ec instanceof EditableControl = true

Thus, for $ c instanceof returns true only for the class Control, for $ ec instanceof returns true only for Editable, Control, EditableControl. For null, always return false.

Static function variables

Variables inside a function can be declared static. The static variable of a function is a common variable for all calls to this function. Static variable is approximately equal to the global variable used only inside the function.

Optional pass-through function parameters

Parameters passed by reference in PHP4 cannot have a default value. This makes it impossible to make a function with an optional object parameter. But the public demanded and in PHP5 it was possible to set the default object value for the object parameter. It should be noted that the only possible default value for such parameters is null.

Example of use:

class Unrequired {
...
}

function myFunction (Unrequired $ param = null) {
...
}

myFunction ();
myFunction (new Unrequired ());

Event function when creating an object of unknown class (__autoload ())

PHP does not keep the entire application in memory. Moreover, for each page it reloads all files with code and converts them into a convenient form. It is well helped by various PHP code accelerators, which store a php page directly converted into executable code. But even in the case of using such an optimizer, it is undesirable to connect all files with classes and functions that may be needed, but are not really used to the script. Setting up the connection of only the necessary classes to each specific page is an exercise that requires great care and causes a great desire to automate it in some way.

Perhaps that is why an event function was introduced with the name __autoload (), which is triggered when an attempt is made to access an unknown class or interface. An appeal is an attempt to create an object of a class, create a descendant class based on a class, and create a class that implements an interface.

Another problem that __autoload solves is the placement of file inclusions in the order of inheritance hierarchy. For example, if MyClass1 is in the file MyClass1.php, and MyClass2 is in the file MyClass2.php and MyClass2 extends MyClass1, then use include to connect them only in the order inlude ('MyClass1.php'); include ('MyClass2.php'); When 2 files - not scary. But when there are several dozen of them, this is already more difficult.

And finally, an example of using __autoload:

test.php ============================
function __autoload ($ name) {
include_once ('classes /'. $ name. '.php');
}

$ t = new Textbox ();

Control.php =========================
class Control {
// ...
}

Textbox.php =========================
class Textbox extends Control {
// ...
}

When you try to create a Textbox, the Textbox.php file will be loaded. Since Textbox extends Control, Control.php will be immediately loaded.

Event functions when accessing a class property (__get (), __set ())

The __get and __set functions can be viewed as the ability to implement properties similar to properties in .NET, VBScript (ASP) or VB. But unlike the listed languages ​​(technologies), in PHP __get and __set are executed for all (!) Properties. For example:

Asp
Class myclass
Property Let Value (NewValue)
...
End property

Property Get Value ()
...
End property

Property Let State (NewValue)
...
End property

Property Get State ()
...
End property
End class

Php
class MyClass {
function __get ($ name) {
switch ($ name) {
case 'Value':
...
break;
case 'State':
...
break;
}
}

function __set ($ name, $ value) {
switch ($ name) {
case 'Value':
...
break;
case 'State':
...
break;
}
}
}

Calling the __get () and __set () methods when accessing a property occurs only if a class variable with this name does not exist. If it exists, then as a result of a call from the main program, you can get either an error (if the variable is private or protected) or, in fact, a variable (if it is public).

Property chains ($ myObj-> parent-> value) work correctly. Example:

class Node {
private $ mValue = 1;
private $ mParent;

function __get ($ name) {
switch ($ name) {
case 'Value':
return $ this-> mValue;
case 'Parent':
return $ this-> mParent;
}
}

function __set ($ name, $ value) {
switch ($ name) {
case 'Value':
$ this-> mValue = $ value;
break;
case 'Parent':
$ this-> mParent = $ value;
break;
}
}
}

$ n1 = new Node ();
$ n2 = new Node ();
$ n2-> Parent = $ n1;
$ n1-> Value = 2;
print $ n2-> Parent-> Value; // Displays 2.

Event function when accessing a class method (__call ())

The event function __call () may have been entered along with __get () and __set (). Most likely this function will find its use in the future. For example, it can be used to emulate overload methods:

class SpecialItem {
// ...
}

class GeneralItem {
// ...
}

class Processor {
function processSpecialItem (SpecialItem $ item) {
// ...
}

function processGeneralItem (GeneralItem $ item) {
// ...
}

function __call ($ method, $ attributes) {
if ($ method == 'process' && count ($ attributes) == 1) {
if ($ attributes [0] instanceof GeneralItem)
$ this-> processGeneralItem ($ attributes [0]);
elseif ($ attributes [0] instanceof SpecialItem)
$ this-> processSpecialItem ($ attributes [0]);
}
}
}

$ p = new Processor ();
$ p-> process (new GeneralItem ()); // processGeneralItem would be called.
$ p-> process (new SpecialItem ()); // processSpecialItem would be called.

Iteration over class properties

All class variables available in the current context can be enumerated by the foreach loop. Such an iteration on class properties can be very useful when cloning objects. For example, if you need to create a clone of an object with a large number of class variables, you can do something like this:

class Node {
private $ value;
private $ parent;
...
private $ type;

function __clone () {
foreach ($ that as $ propertyName => $ propertyValue) {
$ this -> $ propertyName = $ propertyValue;
}
}

function setParent ($ value) {$ this-> parent = $ value; }
function getParent () {return $ this-> parent; }
function setValue ($ value) {$ this-> value = $ value; }
function getValue () {return $ this-> value; }
...
function setType ($ value) {$ this-> type = $ value; }
function getType () {return $ this-> type; }
}

$ myNode = new Node ();
$ myNode-> setValue (10);
$ myNextNode = $ myNode -> __ clone ();
print $ myNextNode-> getValue ();

A simple loop very well replaces a large number of assignments and eliminates the need to synchronize assignments when cloning with a list of all class variables. Obviously, this iteration cannot be applied to class properties implemented through the __get () / __ set () function.

Change standard iteration on properties

The standard language elements that allow iteration are arrays. But their shortcomings are obvious - you can mistakenly put an element of another type into an array. You will not add content checking methods, etc. to the array.

For the introduction of additional entities (classes) that allow iteration over their elements, 2 interfaces are provided: IteratorAggregate and Iterator.

interface IteratorAggregate {
function getIterator (); // returns an array or object
}

IteratorAggregate can be used when the data for iteration can be provided in one of the standard PHP constructs that allow iteration: an array or an object that implements Iterator.

An example of using IteratorAggregate with iteration over array elements:

/ **
Comment:
This example in PHP5 beta 3 does not work.
However, the documentation states that getIterator ()
may return an array or an object that implements Iterator.
So to release, I hope, will fix.
* /
class Control implements IteratorAggregate {
private $ controls;
private $ name;

function __construct () {
$ this-> controls = array ();
}

function addControl ($ obj) {
$ this-> controls [$ obj-> getName ()] = $ obj;
}

function setName ($ value) {$ this-> name = $ value; }
function getName () {return $ this-> name; }

// this function from IteratorAggregate
function getIterator () {
return $ this-> controls;
}
}

$ c1 = new Control ();
$ c1-> setName ('userId');
$ c2 = new Control ();
$ c2-> setName ('userName');
$ form = new Control ();
$ form-> addControl ($ c1);
$ form-> addControl ($ c2);

foreach ($ form as $ ctrl) {
echo $ ctrl-> getName (). '
';
}

This option is only an add-on over standard PHP functionality and can be used when all the elements involved in iteration can be obtained before the iteration begins. If this cannot be done (in the case of retrieving records from a database or reading lines from a large file), another iteration extension mechanism is used - an implementation of the Iterator interface.

interface iterator {
function rewind (); // translates iteration to the first element
function next (); // prepares the next element for output
function key (); // returns the key of the current element
function current (); // returns the current item
function hasMore (); // returns true if there are more elements, otherwise false
}

The following example shows an iteration to find the desired item:

class Control {
private $ name;

function setName ($ value) {$ this-> name = $ value; }
function getName () {return $ this-> name; }
}

class Controls implements Iterator {
private $ controls;
private $ controlNames;
private $ num;

function __construct () {
$ this-> controls = array ();
}

function addControl ($ obj) {
$ this-> controls [$ obj-> getName ()] = $ obj;
}

// function uses class iteration capabilities for
// search for control with given name
function findControl ($ name) {
foreach ($ this as $ control) {
if ($ control-> getName () == $ name)
return $ control;
}

return null;
}

// following functions from Iterator
function rewind () {
$ this-> controlNames = array_keys ($ controls);
$ this-> num = 0;
}

function next () {
$ this-> num ++;
}

function key () {
return $ this-> controlNames ($ this-> num);
}

function current () {
return $ this-> controls [$ this-> key ()];
}

function hasMore () {
return $ this-> num controlNames);
}

}

$ c1 = new Control ();
$ c1-> setName ('userId');
$ c2 = new Control ();
$ c2-> setName ('userName');
$ formControls = new Controls ();
$ formControls-> addControl ($ c1);
$ formControls-> addControl ($ c2);

$ userId = $ formControls-> findControl ('userId');
$ userName = $ formControls-> findControl ('userName');

__METHOD__ constant

The __METHOD__ constant is a good addition to the already existing "magic" PHP4 constants: __LINE__, __FILE__, __FUNCTION__ (with PHP4.3.0), __CLASS__ (with PHP4.3.0). Such constants are called magic because they change their value depending on the place of the call. I think it’s quite obvious what they return, except for the difference between __FUNCTION__ and __METHOD__, since the function of the class is its method. Apparently, the PHP5 developers decided that the __FUNCTION__ constants, which return only the name of a function or class method, will not be enough and add the __METHOD__ constant, which returns the name of the class (in lower case) and the name of the method, separated by two colons.

Thus, the following code will display the text "myclass | myMethod | myclass :: myMethod":

Class MyClass {
function myMethod () {
echo __CLASS__. '|' . __FUNCTION__. '|' . __METHOD__;
}
}

$ m = new MyClass ();
$ m-> myMethod ();

__ToString ()

продолжение следует...

Продолжение:


Часть 1 Object Oriented Programming (OOP) in PHP Interfaces, Classes, Abstract Classes, Objects
Часть 2 - Object Oriented Programming (OOP) in PHP Interfaces, Classes, Abstract


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

Running server side scripts using PHP as an example (LAMP)

Terms: Running server side scripts using PHP as an example (LAMP)