This article describes the basic knowledge of objects and classes in PHPV5. it covers inheritance from the most basic concepts, mainly for experienced object-oriented programmers and readers who have not touched on objects. As a PHP programmer, you must know the variables and functions. However, classes and objects may be the same thing. A perfect system can be created without defining a single class. However, even if you decide
This article describes the basic knowledge of objects and classes in PHP V5. it covers inheritance from the most basic concepts, mainly for experienced object-oriented programmers and readers who have not yet touched on objects.
As a PHP programmer, you must know the variables and functions. However, classes and objects may be the same thing. A perfect system can be created without defining a single class. However, even if you decide not to use object-oriented programming in your code, you may still need to understand object-oriented programming. For example, if you use a third-party library, such as a library that can be used through PHP Extension and Application Repository (PEAR), you will find yourself instantiating objects and calling methods.
What are classes and objects?
In short, a class is an independent block or bundle composed of variables and methods. These components are usually combined to implement a single responsibility or a group of responsibilities. In this article, you will create a class that collects methods for querying and filling a dictionary consisting of items and values.
Class can be directly used as a simple way to organize data and functions, just like a group of functions and variables. However, you can ignore the existence of a class. Class can be used to generate multiple instances in the memory. Such an instance is called an object. Each object can access the same group of functions (called methods in the object-oriented context) and variables (called features or instance variables ), however, the actual values of each variable are different in each object.
Consider a unit in a role-playing game, such as a tank. Classes may set a set of variables for tanks: defense and attack capabilities, scope, health status, and so on. This class may also define a group of functions, including move () and attack (). When the system contains a tank class, this class can be used to generate dozens or hundreds of tank objects, each of which has its own health condition or range characteristics. Therefore, a class is a blueprint or template used to generate an object.
The simplest way to understand classes and objects is to create classes and objects.
First class
You can use the class keyword to create a class. The simplest case is that a class consists of a keyword class, name, and code block:
Class Dictionary {
}
A class name can contain any combination of letters, numbers, and underlines, but cannot start with a number.
In the preceding example, the Dictionary class is completely valid despite its limited use. So how to use this class to create some objects?
$ Obj1 = new Dictionary ();
$ Obj2 = new Dictionary ();
$ Obj3 = new Dictionary ();
At least in form, the instantiated object is similar to the called function. Parentheses must be provided for function calls. Like a function, you need to pass parameters for some classes. You must also use the new keyword. This tells the PHP engine that you want to instantiate a new object. Then, the returned object can be stored in a variable for future use.
Attribute
In the class body, special variables called attributes can be declared. In PHP V4, attributes must be called with the keyword var. This is still a legal syntax, but mainly for backward compatibility. In PHP V5, attributes must be declared as public, private, or protected. Keyword: can we have a bit of privacy here? . However, in this example, all attributes are declared as public. Listing 1 shows a class with two attributes declared.
Listing 1. classes that declare two attributes
Class Dictionary {
Public $ translations = array ();
Public $ type = "En ";
}
As you can see, attributes can be declared and assigned values at the same time. You can use the print_r () function to quickly view the object status. Listing 2 shows that the Dictionary object now has more members.
List 2. list of Dictionary objects
$ En = new Dictionary ();
Print_r ($ en );
If you run the script, you will see the output of the following objects:
Dictionary Object
(
[Translations] => Array
(
)
[Type] => En
)
You can use the object operator-> to access public object attributes. Therefore, $ en-> type indicates the $ type attribute of the Dictionary object referenced by $ en. If you can access the attribute, you can set and obtain its value. The code in listing 3 creates two instances of the Dictionary class-in other words, it instantiates two Dictionary objects. It changes the $ type attribute of an object and adds the translation of two objects:
Listing 3. create two instances of the Dictionary class
$ En = new Dictionary ();
$ En-> translations ['tree'] = "TREE ";
$ Fr = new Dictionary ();
$ Fr-> type = "Fr ";
$ Fr-> translations ['tree'] = "arbre ";
Foreach (array ($ en, $ fr) as $ dict ){
Print "type: {$ dict-> type }";
Print "TREE: {$ dict-> translations ['tree']} \ n ";
}
The script output is as follows:
Type: En TREE: tree
Type: Fr TREE: arbre
Therefore, the Dictionary class is useful now. A single object can store different key-value combinations, and a flag that tells the client about the details of such a Dictionary.
Although the Dictionary class is almost the same as the package of the associated array, there are some clues about the object function. Currently, we can represent the sample data, as shown in listing 4.
Listing 4. sample data
$ En = array (
'Translation' => array ('tree' => 'tree '),
'Type' => 'en'
);
$ Fr = array (
'Translation' => array ('tree' => 'bre '),
'Type' => 'Fr'
);
Although the data structure achieves the same purpose as the Dictionary class, it does not provide structure guarantee. If a Dictionary object is passed, we know that it has the $ translations attribute. However, this is not guaranteed if it is an associated data. This fact makes it a bit of luck to query $ fr ['translation'] ['tree']; unless the code for the query determines the origin of the array. This is the focus of the object: the object type is the guarantee of its features.
Although OSS has the advantages of data storage, you may not feel it at all. Objects can be things, but the key is that they can also do things.
Method
In short, the method is a function declared in the class. They are usually (but not always) called through object instances using object operators. Listing 5 adds a method to the Dictionary class and calls this method.
Listing 5. add a method to the Dictionary class
Class Dictionary {
Public $ translations = array ();
Public $ type = "En ";
Function summarize (){
$ Ret = "Dictionary type: {$ this-> type} \ n ";
$ Ret. = "Terms:". count ($ this-> translations). "\ n ";
Return $ ret;
}
}
$ En = new Dictionary ();
$ En-> translations ['tree'] = "TREE ";
Print $ en-> summarize ();
It provides the following output:
Dictionary type: En
Terms: 1
As we can see, the method for declaring the summarize () method is the same as that for declaring any function, except that it is declared in the class. The summarize () method is called by using object operators in a Dictionary instance. Summarize () function access attribute to provide a brief description of the object state.
Note the usage of a new feature in this article. $ This pseudo variable provides a mechanism for an object to reference its own attributes and methods. Outside the object, you can use a handle to access its elements ($ en in this example ). This handle does not exist in the object, so you must resort to $ this. If you think $ this is confusing, try to replace it with the current instance in your mind when encountering it in code.
Classes are usually represented in charts using the Universal Modeling Language (UML. UML details are beyond the scope of this article, but such charts are just a good way to visualize class relationships. Figure 1 shows the Dictionary class in UML. The class name is at the top layer, the attribute is in the middle, and the method is at the bottom layer.
Figure 1. use UML to display the Dictionary class
Constructor
The PHP engine recognizes many "magic" methods. If methods are defined, the PHP engine will automatically call these methods when appropriate circumstances occur. The most commonly implemented method is the constructor method. The PHP engine calls constructors when instantiating objects. All the basic settings code of the object is placed in the constructor. In PHP V4, create a constructor by declaring the same name as the class. In V5, the method called _ construct () should be declared. Listing 6 shows the constructors for the DictionaryIO object.
Listing 6. constructor requiring DictionaryIO objects
Class Dictionary {
Public $ translations = array ();
Public $ type;
Public $ dictio;
Function _ construct ($ type, DictionaryIO $ dictio ){
$ This-> type = $ type;
$ This-> dictio = $ dictio;
}
//...
To instantiate a Dictionary object, you must pass the type string and DictionaryIO object to its constructor. Constructors use these parameters to set their own attributes. The following code instantiates a Dictionary object: $ en = new Dictionary ("En", new DictionaryIO ());
The Dictionary class is safer than before. All Dictionary objects have been initialized with required parameters.
Of course, you cannot prevent others from changing the $ type attribute or setting $ dictio to null. Fortunately, PHP V5 can help you implement this function.
Keyword: can we have a bit of privacy here?
You can see the public keywords related to the attribute declaration. This keyword indicates the visibility of attributes. In fact, the attribute visibility can be set to public, private, and protected. Attributes declared as public can be written and read outside the class. attributes declared as private are only visible in the context of objects or classes. Attributes declared as protected can only be visible in the context of the current class and its subclass. (In the inheritance section, you will see that this content works .) You can use the private attribute to actually lock the class. If you declare an attribute as private and attempt to access it from outside the class scope (as shown in listing 7), the PHP engine will throw a fatal error.
Listing 7. attempting to access attributes from outside the class scope
Class Dictionary {
Private $ translations = array ();
Private $ dictio;
Private $ type;
Function _ construct ($ type, DictionaryIO $ dictio ){
$ This-> type = $ type;
$ This-> dictio = $ dictio;
}
//...
}
$ En = new Dictionary ("En", new DictionaryIO ());
$ En-> dictio = null;
Output: Fatal error: Cannot access private property
Dictionary: $ dictio in...
In general, most attributes should be declared as private, and then methods for obtaining and setting these attributes should be provided as needed. In this way, you can control the class interface, make some data read-only, clean up or filter parameters before assigning parameters to properties, and provide a set of clear rules for interacting with objects.
The method for modifying method visibility is the same as that for modifying attribute visibility, that is, adding public, private, or protected to the method declaration. If the class needs to use some housework management methods that do not need to be known in the external world, it can be declared as private. In listing 8, the get () method provides a translation extraction interface for users of the Dictionary class. This class also needs to track all queries, so the private method logQuery () is provided ().
Listing 8. the get () method provides interfaces for users of the Dictionary class.
Function get ($ term ){
$ Value = $ this-> translations [$ term];
$ This-> logQuery ($ term, $ value, "get ");
Return $ value;
}
Private function logQuery ($ term, $ value, $ kind ){
// Write log information
}
Declaring logQuery () as private simplifies the public interface and prevents the class from calling logQuery () improperly (). Like an attribute, attempting to call a private method from outside the include class results in a fatal error.
Perform operations in class context
So far, the methods and attributes you see are all operated in the object context. That is to say, you must use an object instance to access methods and properties through the $ this pseudo variable or the object reference stored in the standard variable. Sometimes, you may find it more useful to access attributes and methods through classes rather than object instances. This type of members is called static members.
To declare static attributes, place the keyword static behind the visibility modifier and directly before the attribute variable.
The following example shows a single static attribute: $ iodir, which stores the path of the default directory for saving and reading Dictionary data. Because the data is the same for all objects, it makes sense to make it available for all instances.
Listing 9. single static $ iodir attribute
Class Dictionary {
Public static $ iodir = ".";
//...
}
You can use the range resolution operator to access static attributes. this operator consists of double colons. The range resolution operator should be located between the class name and the static attribute to be accessed.
Print Dictionary: $ iodir. "\ n ";
Dictionary: $ iodir = "/tmp ";
As you can see, you do not need to instantiate a Dictionary object to access this property.
The declaration and the syntax for accessing static methods are similar. Again, put the static keyword after the visibility modifier. Listing 10 shows two static methods that access the $ iodir attribute declared as private.
Listing 10. two static methods for accessing the $ iodir attribute
Class Dictionary {
Private static $ iodir = ".";
//...
Public static function setSaveDirectory ($ dir ){
If (! Is_dir ($ dir) |
! Is_writable ($ dir )){
Return false;
}
Self: $ iodir = $ dir;
}
Public static function getSaveDirectory (){
Return self: $ iodir;
}
//...
}
You can no longer access the $ iodir directory. By creating special methods to access properties, you can ensure that any value provided is sound. In this example, the method checks whether a given string points to a writable directory before being allocated.
Note that both methods use the keyword self and the access parsing operator to reference the $ iodir attribute. $ This cannot be used in static methods, because $ this is a reference to the current object instance, but static methods are called through classes rather than through objects. If the PHP engine sees $ this in a static method, it throws a fatal error and a prompt message.
To call a static method from outside the class, you can add a range parser and method name to the class name.
Dictionary: setSaveDirectory ("/tmp ");
Print Dictionary: getSaveDirectory ();
There are two important reasons for using static methods. First, the utility operation may not require an object instance to do its work. The statement is static, saving the workload of creating objects for client code. Second, the static method is globally available. This means that you can set a value that can be accessed by all object instances, and make the static method a good way to share key data on the system.
Although static attributes are usually declared as private to prevent others' intervention, there is a way to create a read-only static range attribute, that is, to declare constants. Like global attributes, class constants cannot be changed once defined. It is used for status signs and other things that do not change during the process lifecycle, such as pi or all countries in Africa.
Declare a class constant with the const keyword. For example, because there is almost certainly a database behind the actual implementation of a Dictionary object, we can also assume that the term and translation have a maximum length. Set it to a class constant in listing 11.
Listing 11. set MAXLENGTH to a class constant
Class Dictionary {
Const MAXLENGTH = 250;
//...
}
Print Dictionary: MAXLENGTH;
Class constants are always public, so visibility keywords cannot be used. This is a problem because any attempt to change its value will cause a parsing error. Note that, unlike conventional attributes, class constants do not start with the dollar sign. Inheritance
If you are familiar with object-oriented programming, you will know that I keep the best at the end. Class and the relationship between the generated dynamic objects make the system more flexible. For example, each Dictionary object encapsulates different sets of translation data, but the models of these different entities are defined in a single Dictionary class.
However, you sometimes need to note the differences at the class level. Do you remember the DictionaryIO class? It obtains data from a Dictionary object, writes it to the file system, retrieves data from a file, and merges it into a Dictionary object. Listing 12 shows how to use serialization to save and load Dictionary data.
Listing 12. fast implementation using serialization
Class Dictionary {
//...
Function asArray (){
Return $ this-> translations;
}
Function getType (){
Return $ this-> type;
}
Function export (){
$ This-> dictio-> export ($ this );
}
Function import (){
$ This-> dictio-> import ($ this );
}
}
Class DictionaryIO {
Function path (Dictionary $ dictionary, $ ext ){
$ Path = Dictionary: getSaveDirectory ();
$ Path. = DIRECTORY_SEPARATOR;
$ Path. = $ dictionary-> getType (). ". $ ext ";
Return $ path;
}
Function export (Dictionary $ dictionary ){
$ Translations = $ dictionary-> asArray ();
File_put_contents ($ this-> path (
$ Dictionary, 'Serial '),
Serialize ($ translations ));
}
Function import (Dictionary $ dictionary ){
$ Path = $ this-> path ($ dictionary, 'Serial ');
If (! Is_file ($ path) return false;
$ Translations = unserialize (
File_get_contents ($ path ));
Foreach ($ translations as $ term => $ trans ){
$ Dictionary-> set ($ term, $ trans );
}
}
}
$ Dict = new Dictionary ("En", new DictionaryIO ());
$ Dict-> set ("TREE", "tree ");
$ Dict-> export ();
This example introduces two simple Dictionary methods. Specifically, asArray () returns a copy of the $ translations array. The implementation of DictionaryIO has the advantages of simplicity. This is because the error check is usually omitted in the sample code. Even so, it is still a quick and easy way to save data to a file.
Once such a library is deployed, you must immediately support its storage format. Making the format obsolete will offend those users who may store the backup in this way. However, the requirement has changed, and users may complain that the output format is not convenient for editing. These users want to send exported files to third parties in XML format.
Now we are faced with a problem. How does one support two formats in the DictionaryIO interface?
A solution is to use conditional statements in the export () and import () methods to test the type flag, as shown in listing 13.
Listing 13. using conditional statements in the export () and import () methods
Function export (Dictionary $ dictionary ){
If ($ this-> type = DictionaryIO: SERIAL ){
// Write serialized data
} Else if ($ this-> type = DictionaryIO: XML ){
// Write xml data
}
}
Function import (Dictionary $ dictionary ){
If ($ this-> type = DictionaryIO: SERIAL ){
// Read serialized data
} Else if ($ this-> type = DictionaryIO: XML ){
// Read xml data
}
}
This structure is an example of a bad "code taste" because it relies on replication. To make changes in one place (for example, to add a new type test), you need to make a set of changes elsewhere (to bring other types of tests into the row ), the code will soon become error-prone and hard to read.
Inheritance provides a more elegant solution. You can create a new XmlDictionaryIO class that inherits the interfaces set by DictionaryIO, But overrides some of these functions.
Use the extends keyword to create a subclass. The following is the minimum implementation of the XmlDictionaryIO class:
XmlDictionaryIO extends DictionaryIO {
}
The current functions of XmlDictionaryIO are exactly the same as those of DictionaryIO. Because it inherits all the public (and protected) attributes from DictionaryIO, you can apply the same operations applied to the DictionaryIO object to the XmlDictionaryIO object. This relationship is extended to the object type. The XmlDictionaryIO object is obviously an instance of the XmlDictionaryIO class, but it is also an instance of DictionaryIO-similarly, in a general order, a person is both human, mammal, and animal. You can use the instanceof operator to test this. if the object is a member of a specified class, true is returned, as shown in listing 14. Listing 14. Using the instanceof operator to test inheritance
$ Dictio = new XmlDictionaryIO ();
If ($ dictio instanceof XmlDictionaryIO ){
Print "object is an instance of XmlDictionaryIO \ n ";
}
If ($ dictio instanceof DictionaryIO ){
Print "object is an instance of DictionaryIO \ n ";
}
The output is as follows:
Object is an instance of XmlDictionaryIO
Object is an instance of DictionaryIO
Just as instanceof accepts $ dictio as a DictionaryIO object, the method also accepts these objects as parameters. This means that the XmlDictionaryIO object can be passed to the Dictionary class constructor, even if DictionaryIO is of the type specified by the constructor signature.
Listing 15 shows the fast and dirty implementation of XmlDictionaryIO, using DOM to complete XML functions.
Listing 15. XmlDictionaryIO implementation
Class XmlDictionaryIO extends DictionaryIO {
Function export (Dictionary $ dictionary ){
$ Translations = $ dictionary-> asArray ();
$ Doc = new DOMDocument ("1.0 ");
$ Dic_el = $ doc-> createElement ("dictionary ");
$ Doc-> appendChild ($ dic_el );
Foreach ($ translations as $ key => $ val ){
$ Term_el = $ doc-> createElement ("term ");
$ Dic_el-> appendChild ($ term_el );
$ Key_el = $ doc-> createElement ("key", $ key );
$ Val_el = $ doc-> createElement (
"Value", $ val );
$ Term_el-> appendChild ($ key_el );
$ Term_el-> appendChild ($ val_el );
}
File_put_contents ($ this-> path (
$ Dictionary, 'xml '),
$ Doc-> saveXML ());
}
Function import (Dictionary $ dictionary ){
$ Path = $ this-> path ($ dictionary, 'xml ');
If (! Is_file ($ path) return false;
$ Doc = DOMDocument: loadXML (
File_get_contents ($ path ));
$ Termlist = $ doc
-> GetElementsByTagName ("term ");
Foreach ($ termlist as $ term ){
$ Key = $ term-> getElementsByTagName ("key ")
-> Item (0)-> nodeValue;
$ Val = $ term
-> GetElementsByTagName ("value ")
-> Item (0)-> nodeValue;
$ Dictionary-> set ($ key, $ val );
}
}
}
For more information about obtaining and generating XML, see. There are many ways to do this, including the perfect SimpleXML extension. In short, the import () method takes the XML document as the parameter and uses it to fill the Dictionary object. The export () method retrieves data from a Dictionary object and writes it to an XML file. (In the real world, XLIFF-based XML format may be used, which is suitable for importing to a third-party translation tool .)
Note: Both import () and export () call the utility method path (). This method does not exist in the XmlDictionaryIO class. But it does not matter, because path () is implemented in DictionaryIO. When XmlDictionaryIO implements a method, the implementation is called for the XmlDictionaryIO object when the method is called. If no implementation exists, the call failure is returned to the parent class.
Figure 2 shows the inheritance relationship between DictionaryIO and XmlDictionaryIO. The closed arrow indicates inheritance, pointing from the subclass to the parent class.
Figure 2. inheritance relationship
Conclusion
Due to limited space, it is impossible to describe them all. Further research has two directions: breadth and depth. Breadth refers to the features beyond the scope of this article, such as abstract classes, interfaces, iterator interfaces, reflection, exceptions, and object replication. Depth refers to design issues. Although understanding the scope of tools that PHP can use for object-oriented programming is important, it is equally important to consider how best to use these features. Fortunately, there are a lot of available references dedicated to the object-oriented context design model (see "references ")
If you are still using PHP V4 programming, I hope you can find new features to prove that it is correct to migrate to V5 and its core object-oriented features. Soon you will be surprised to find that you cannot leave them.