PHP object-oriented understanding-PHP source code

Source: Internet
Author: User
Tags php server netbeans
Ec (2); & nbsp; paradigm limitations each programming paradigm limits our ability to turn imagination into reality. While some feasible schemes are removed from these paradigms, they are included as alternatives, but all of them are intended to achieve the same expression effect. Modular programming restricts the program scale, forcing programmers to apply their skills only within the scope of the corresponding module, and each module should end with & ldquo; go-to & rdquo; to point to other modules. This setting directly affects the size of the finished program. In addition, structured programming and procedural programming methods, script ec (2) and script

Limitations

Each programming paradigm limits our ability to turn our imagination into reality. While some feasible schemes are removed from these paradigms, they are included as alternatives, but all of them are intended to achieve the same expression effect. Modular programming restricts the program scale and forces programmers to apply their skills only within the scope of the corresponding module, and each module must end with "go-to" to point to other modules. This setting directly affects the size of the finished program. In addition, the "go-to" statement is removed from structured and procedural programming, which limits the programmer's ability to adjust the sequence, selection, and iteration statements. The sequence belongs to the variable assignment, and the selection belongs to the if-else judgment, while the iteration belongs to the do-while loop. These have become the cornerstone of today's programming languages and paradigms.

The object-oriented programming method removes the function pointer and introduces the polymorphism feature. PHP uses pointers in a different way than C, but we can still find the Variant Form of these function pointers from the variable function library. This allows the programmer to regard the value of a variable as the function name to implement the following:

function foo() {      echo "This is foo";  }function bar($param) {      echo "This is bar saying: $param";  }  $function = 'foo';  $function();        // Goes into foo()  $function = 'bar';  $function('test');  // Goes into bar()

At first glance, this feature seems irrelevant. But if you think about it, you will surely find that it contains extremely powerful potential. We can send a variable as a parameter to a function, and then let the function call other functions based on the parameter value. This is definitely tricky. It enables us to call functions without understanding the functions of the function, and the function itself does not reflect any difference.

This technology is also the key to implementing polymorphism calls.

Now, let's not talk about the function pointer function. Let's take a look at its working mechanism. In fact, the "go-to" declaration has been hidden in the function pointer, or at least indirectly achieves the execution effect similar to "go-. This is not good news. In fact, PHP implements the "go-to" declaration without using it directly. As shown in statement, I need to make a statement in PHP first. Although this may seem difficult to understand, it is difficult for us to make accurate judgments when there are many types of large projects and functions that are associated with each other. On the C language side, this relationship becomes more obscure and difficult to understand.

However, eliminating function pointers alone is far from enough. The object-oriented programming mechanism will inevitably bring about an alternative solution. In fact, it also contains polymorphism and a set of simple syntax. The point is that polymorphism is the core value of object-oriented programming, that is, the dependency between control flow and source code is the opposite.

In the image above, we have illustrated a simple example of how polymorphism works between two different paradigms. In the field of procedural or structured programming, the control flow is very similar to the source code in terms of dependency-both point to more specific output behavior.

In the aspect of object-oriented programming, we can reverse the dependency between source code to point to abstract execution results, and keep the control flow still pointing to specific execution results. This is important because we want the control mechanism to touch the unstable part of the specific layer and code as much as possible so that we can truly make the execution results consistent with expectations. But on the source code side, our requirements are the opposite. For the source code, we hope to exclude the specific results and unstable factors, so as to simplify the modification process and minimize the impact of changes on other code. In this way, the unstable part can be corrected frequently, but the abstract part is still valid. You can click here to read the Dependency inversion principle research paper written by Robert C. Martin.

Pilot task

In this chapter, we will create a simple application to list Google calendars and event reminders. First, we try to develop in a programmatic way, which only involves simple functions and avoids using classes or objects in any form. After the development is complete, we further organize the Code through behaviors without modifying the Procedural Code. Finally, we try to convert it into an object-oriented version.

Google php api Client

Google provides an API client for PHP. We will use it to connect to our Google account to operate the calendar service. To make the Code effective correctly, you need to set your Google account to accept queries from the calendar.

Although this is an important prerequisite for this guide, it cannot be considered as the main content. To avoid wasting too much space in this area, please refer to the official instructions. You don't have to worry about it. The configuration process is very simple and can be completed in about five minutes.

The sample code included in this tutorial contains the Google php api client code. We recommend that you use this set to ensure that the entire learning process is consistent with the article description. In addition, if you want to install it on your own, click here to view the official instructions.

Next, enter the information in the apiAccess. php file as instructed. This file is used in both procedural and object-oriented instances, so you do not have to fill it out again in the new version. I left my own content in the file, so that you can easily find the corresponding location and modify it according to your own information.

If you happen to use NetBeans, I save the project files in folders containing different examples. In this way, you can easily open the Project and click Run --> Run Project to Run directly on the local PHP server (PHP 5.4 is required.

The client library connected to Google API is object-oriented. For the normal running of the example, I have compiled a small collection of functions, which includes all the functions required in this tutorial. In this way, we can use the procedural layer to write software on the object-oriented client library without involving any objects.

If you want to quickly test whether your code works properly with the connection to Google APIs, you can directly use the code in the index. php file. It lists all calendar information in the account, and should contain at least one set of calendar with the summary field containing your name. If there is a contact's birthday information in the calendar, Google API cannot work with it. But you don't have to worry about it. Just select another one.

require_once './google-api-php-client/src/Google_Client.php';  require_once './google-api-php-client/src/contrib/Google_CalendarService.php';  require_once __DIR__ . '/../apiAccess.php';  require_once './functins_google_api.php';  require_once './functions.php';  session_start();  $client = createClient();  if(!authenticate($client)) return;  listAllCalendars($client);

This index. php file will become the entry point of our application. We will not use any Web framework or other complex mechanisms. All we need to do is simply output some HTML code.

Programmatic Development Solution

Now we know the target to be created and the resources we can use. Next we will download the source code in the attachment. I will provide useful snippets in the code, but in order to further understand the global situation, you may want to access its initial source.

In this solution, we only want the results to take effect as expected. Our code may look a little rough and only involves the following files:

Index. php-This is the only file that needs to be accessed directly through a browser and directed to its GET parameter.

Functions_google_api.php-Include all the Google APIs mentioned above.

Functions. php-All miracles happen here.

Functions. php will accommodate all the execution processes of the application. Including the routing logic, performance, and all values and actions occur here. This application is very simple and its main logic is shown in:

Here is a function named doUserAction (). Whether it takes effect depends on a long if-else statement. Other Methods Determine the call situation based on the parameters in the GET variable. These methods then connect to Google's calendar using APIs and display any results we need on the screen.

function printCalendarContents($client) {     putTitle('These are you events for ' . getCalendar($client, $_GET['showThisCalendar'])['summary'] . ' calendar:');     foreach (retrieveEvents($client, $_GET['showThisCalendar']) as $event) {        print('

'. Date ('Y-m-d H: m', strtotime ($ event ['created']); putLink ('? ShowThisEvent = '. htmlentities ($ event ['id']). '& calendarId = '. htmlentities ($ _ GET ['showthiscalendar ']), $ event ['summary']); print ('

'); Print ('
');}}

This example is probably the most complex function in our code. It calls a helper function named putTitle (), which serves to output some formatted HTML output to act as the title. The title will contain the actual name of our calendar, which is implemented by calling the getCalendar () function from the functions_google_api.php file. The returned calendar information is an array containing a summary field, which is exactly what we are looking.

$ Client variables are passed to all our functions. It needs to be connected to Google API, but we will talk about it later.

Next, we will sort out all the existing events in the calendar. This list is obtained by running API requests encapsulated in the retrieveEvents () function. For each event, the creation date and title are displayed.

The rest of the code is similar to or even easier to understand as we have discussed earlier. You can take a look at it with ease and then enter the next chapter.

Organize Procedural Code

Our current code is okay, but I think we can adjust it to make it more appropriate. You may have found from the source code that is included that all the code that has been organized in the project is named "GoogleCalProceduralOrganized ".

Use global client Variables

In code organization, the first disturbing thing is that we promote $ client variable as a parameter to the global layer and the deep layer of nested functions. Programmatic programming provides a clever solution for such situations, that is, global variables. Since $ client is defined by index. php, from a global perspective, all we need to change is how the function uses this variable. Therefore, we do not need to change the $ client parameter, but only need to do the following:

function printCalendars() {     global $client;     putTitle('These are your calendars:');     foreach (getCalendarList($client)['items'] as $calendar) {        putLink('?showThisCalendar=' . htmlentities($calendar['id']), $calendar['summary']);        print('');     }  }

You may wish to compare the existing code with the code finished in the attachment to see what is different between the two. Yes, we didn't pass $ client as a parameter. Instead, we used global $ client in all functions and passed it as a parameter that only passed to Google API functions. From a technical point of view, even Google API functions can use the $ client variable from the global, but I think it is best to maintain API independence as much as possible.

Separated representation from logic

Some functions are very specific-they are only used to output information on the screen, but some functions are used to determine the trigger conditions. Some functions have two roles. In the face of this situation, we often better place these functions with special purposes in our own files. First, sort out the functions that are only used for screen information output and transfer them to the functions_display.php file. The procedure is as follows:

function printHome() {     print('Welcome to Google Calendar over NetTuts Example');  }  function printMenu() {     putLink('?home', 'Home');     putLink('?showCalendars', 'Show Calendars');     putLink('?logout', 'Log Out');     print('');  }  function putLink($href, $text) {     print(sprintf('%s | ', $href, $text));  }  function putTitle($text) {     print(sprintf('
% S

', $ Text);} function putBlock ($ text) {print ('

'. $ Text .'

');}

To complete the remaining representation separation, We need to extract the representation from the method. The following uses a single method as an example to demonstrate this process:

function printEventDetails() {     global $client;     foreach (retrieveEvents($_GET['calendarId']) as $event)        if ($event['id'] == $_GET['showThisEvent']) {           putTitle('Details for event: '. $event['summary']);           putBlock('This event has status ' . $event['status']);           putBlock('It was created at ' .                 date('Y-m-d H:m', strtotime($event['created'])) .                 ' and last updated at ' .                 date('Y-m-d H:m', strtotime($event['updated'])) . '.');           putBlock('For this event you have to ' . $event['summary'] . '.');        }  }

We can see that no matter what the content in the if statement is, the Code belongs to the Code, and the rest is the business logic. Instead of using a large function to process all transactions, we prefer to split it into multiple different functions:

function printEventDetails() {     global $client;     foreach (retrieveEvents($_GET['calendarId']) as $event)        if (isCurrentEvent($event))           putEvent($event);  }  function isCurrentEvent($event) {     return $event['id'] == $_GET['showThisEvent'];  }

After the separation is completed, the business logic becomes easy to understand. We even extracted a small method to check whether the event is the current event. All codes are now under the responsibility of the putEvent ($ event) function and saved in the functions_display.php file:

function putEvent($event) {     putTitle('Details for event: ' . $event['summary']);     putBlock('This event has status ' . $event['status']);     putBlock('It was created at ' .           date('Y-m-d H:m', strtotime($event['created'])) .           ' and last updated at ' .           date('Y-m-d H:m', strtotime($event['updated'])) . '.');     putBlock('For this event you have to ' . $event['summary'] . '.');  }

Although this method is only responsible for displaying information, its function still needs to be implemented only when you have a good understanding of the $ event structure. However, this is enough for our simple instance. For other methods, you can separate them in a similar way.

Clear long if-else statements

Currently, the final step of code sorting is the long if-else declaration in the doUserAction () function, which determines the actual processing method of each action. PHP has excellent flexibility in metaprogramming (calling functions through reference. This feature enables us to associate the value of the $ _ GET variable with the function name. In this way, we can introduce a separate action parameter in the $ _ GET variable and use this value as the function name.

function doUserAction() {     putMenu();     if (!isset($_GET['action'])) return;        $_GET['action']();  }

In this way, the generated menu is as follows:

function putMenu() {     putLink('?action=putHome', 'Home');     putLink('?action=printCalendars', 'Show Calendars');     putLink('?logout', 'Log Out');     print('');  }

As you can see, after reorganizing the code, the code has presented the object-oriented design feature. Although we do not know what object it is targeting and what exact behavior it will execute, its features have begun to emerge.

We have made the data types from the business logic a decisive factor in representation. The effect is similar to the dependency inversion mechanism we mentioned in the first introduction. The direction of the control flow is still from the business logic to the representation, but the source code dependency is the opposite. From this point of view, I think the entire mechanism is more like a two-way dependency system.

Object-Oriented Design tends to be reflected in another aspect, that is, we have almost no involvement in metaprogramming. We can call a method, but we don't know anything about it. This method can have any content, and the process is very similar to that of low-level polymorphism.

Dependency Analysis

For the current code, we can draw a diagram, the content is as follows. Through this graph, we can see the first few steps of the application program running process. Of course, it is too complicated to draw the entire process.

Blue lines represent program calls. As you can see, these lines always point in the same direction. The green lines in the figure indicate indirect calls. You can see that all indirect calls must go through the doUserAction () function. These two lines represent the control flow. Obviously, the direction of the control flow remains unchanged.

The red lines introduce completely different concepts, which represent the original source code dependency. The reason for saying "first" is that as the application runs, it becomes increasingly complicated and difficult to grasp. The putMenu () method contains the name of the function called by the specific link. This is a dependency and is also a basic rule for creating methods for all other relationships. The specific relationship between them depends on the behavior of other functions.

We can also see another dependency on data. I have mentioned $ calendar and $ event before, and the output function needs to understand the internal structure of these arrays to implement the established functions.

After completing the above content, we are fully prepared to face the last challenge in this tutorial.

Object-oriented Solution

No matter which paradigm we adopt, we cannot find a perfect solution to the problem. Therefore, the following code is only my personal suggestion.

Start with intuition

We have completed the separation of business logic and performance, and even used the doUserAction () method as an independent unit. My intuition is to first create three classes: Presenter, Logic, and Router. All three may need to be adjusted later, but let's start from here, right?

The Router contains only one method, and the implementation method is very similar to the method mentioned earlier.

class Router {     function doUserAction() {        (new Presenter())->putMenu();        if (!isset($_GET['action']))           return;        (new Logic())->$_GET['action']();     }  }

What we need to do now is to call the putMenu () method using the Presenter object we just created, and call it using the Logic object for other behaviors. But this will immediately cause problems-one of our actions is not included in the Logic class. PutHome () exists in the Presenter class. We need to introduce an action in Logic to delegate the putHome () method to the Presenter. Remember, all we need to do now is to organize the existing code into three classes and use the three as alternative objects for object-oriented design. All we do now is to make the design scheme work properly. After the code is compiled, we will further debug it.

After we introduced the putHome () method into the Logic class, we encountered new challenges. How can I call a method from a Presenter? We can create a Presenter object and pass it to Logic. Next we will start with the Router class.

class Router {     function doUserAction() {        (new Presenter())->putMenu();        if (!isset($_GET['action']))           return;        (new Logic(new Presenter))->$_GET['action']();     }  }

Now we can add a constructor to Logic and add it to the delegate pointing to putHome () in Presenter.

class Logic {     private $presenter;     function __construct(Presenter $presenter) {        $this->presenter = $presenter;     }     function putHome() {        $this->presenter->putHome();     }  [...]  }

Through the index. some minor adjustments of php allow Presenter to include the original display method, Logic to include the original business Logic function, and Router to include the original behavior selector, we can make our code run normally and have the "Home" menu element.

require_once './google-api-php-client/src/Google_Client.php';  require_once './google-api-php-client/src/contrib/Google_CalendarService.php';  require_once __DIR__ . '/../apiAccess.php';  require_once './functins_google_api.php';  require_once './Presenter.php';  require_once './Logic.php';  require_once './Router.php';  session_start();  $client = createClient();  if(!authenticate($client)) return;  (new Router())->doUserAction();

The following figure shows the execution result.

Next, we need to change the Calling command pointing to the display Logic in the Logic class to conform to $ this-> presenter. Now we have two methods -- isCurrentEvent () and retrieveEvents () -- they are only used inside the Logic class. We use it as a dedicated method and change the call relationship accordingly.

Next we will perform the same processing on the Presenter class, and change all calls pointing to methods to direct to $ this-> something. Since putTitle (), putLink (), and putBlock () are only used by the Presenter, You need to convert them into private. If the above change process is difficult to understand and operate, please check the completed code in the GoogleCalObjectOrientedInitial folder in the source code of the attachment.

Now our application can run normally. The programmatic Code Compiled by the object-oriented syntax still uses the $ client global variable, and has a lot of other non-object-oriented features-but still can run normally.

If you want to draw a dependency class diagram for the current Code, it should be as follows:

The dependency between control flow and source code is based on Router, Logic, and presentation layer. The last change weakens the Dependency inversion feature we observed in the previous step, but do not be confused-the principle remains the same. What we need to do is to make it clearer.

Restore source code dependency

It is difficult to define which one of the fundamental principles is more important, but I think the Dependency inversion principle has the greatest and most direct impact on our application design. This principle stipulates:

A: High-Level modules should not depend on low-level modules. Both of them should depend on abstraction.

B: Abstraction should not depend on details, but on abstraction.

Simply put, this means that the specific implementation should depend on the abstract class. Classes tend to be abstract, and they are less prone to changes. Therefore, we can understand that frequently changed classes should depend on other more stable classes. Therefore, the most unstable part of any application is probably the user interface, which is implemented through the Presenter class in our application example. Let's clarify the Dependency inversion process.

First, let the Router only use the Presenter and break its dependency on Logic.

class Router {     function doUserAction() {        (new Presenter())->putMenu();        if (!isset($_GET['action']))           return;        (new Presenter())->$_GET['action']();     }  }

Then we change the Presenter so that it can use the Logic instance and obtain the required information. In our example, I think it is acceptable for the Presenter to establish this Logic instance. However, in the production system, you may usually use Factories to create business Logic related to objects, and inject it into the presentation layer.

Now, the putHome () function that originally exists in both the Logic and Presenter classes will disappear from the Logic. This phenomenon indicates that we have begun to clear duplicate data. Constructors and references pointing to the Presenter are also removed from Logic. On the other hand, the Logic object created by the constructor must be written into the Presenter.

class Presenter {     private $businessLogic;     function __construct() {        $this->businessLogic = new Logic();     }     function putHome() {        print('Welcome to Google Calendar over NetTuts Example');     }  [...]  }

After the preceding changes are complete, Click Show Calendars. An error message is displayed on the screen. Since all the actions within the link point to the function name in the Logic class, we must use more consistency adjustments to restore the dependency between the two. Next we will make one-to-one changes to the method. First, let's look at the first error message:

Fatal error: Call to undefined method Presenter::printCalendars()  in /[...]/GoogleCalObjectOrientedFinal/Router.php on line 9

Our Router wants to call a method that does not exist in the Presenter, that is, printCalendars (). We create this method in Presenter and check the impact it has on Logic. As you can see in the results, it outputs a title and calls putCalendars () again after repeating the loop (). In the Presenter class, the printCalendars () method is as follows:

function printCalendars() {     $this->putCalendarListTitle();     foreach ($this->businessLogic->getCalendars() as $calendar) {        $this->putCalendarListElement($calendar);     }  }

In terms of Logic, this method is very simple-directly calling Google API library.

function getCalendars() {     global $client;     return getCalendarList($client)['items'];  }

This may cause two problems in everyone's mind: "Do we really need the Logic class ?" And "Is there any logic in our application ?" Well, we don't know the answer yet. All we can do now is continue the above process until all the code works normally and Logic no longer depends on Presenter.

Next, we will use the printCalendarContents () method in Presenter, as shown below:

function printCalendarContents() {     $this->putCalendarTitle();     foreach ($this->businessLogic->getEventsForCalendar() as $event) {        $this->putEventListElement($event);     }  }

This in turn allows us to simplify getEventsForCalendar () in Logic and convert it into the following form:

function getEventsForCalendar() {     global $client;     return getEventList($client, htmlspecialchars($_GET['showThisCalendar']))['items'];  }

Now the application no longer reports errors, but I have discovered new problems. The $ _ GET variable is used by both the Logic and Presenter classes -- $ _ GET should be used only by the Presenter class. I mean, because you need to create a link to fill the $ _ GET variable, Presenter must be aware of $ _ GET. This means that $ _ GET is closely related to HTTP. Now, we hope that our code can work together with the command line or desktop graphic user interface.
Therefore, we need to ensure that only the Presenter is aware of this situation. The above two methods will be transformed into the following content:

function getEventsForCalendar($calendarId) {     global $client;     return getEventList($client, $calendarId)['items'];  }  function printCalendarContents() {     $this->putCalendarTitle();     $eventsForCalendar = $this->businessLogic->getEventsForCalendar(htmlspecialchars($_GET['showThisCalendar']));     foreach ($eventsForCalendar as $event) {        $this->putEventListElement($event);     }  }

Now we need to implement the output function of specific events. For the examples in this article, we assume that we cannot directly retrieve any event, that is, we must perform event search in person. The Logic class will come in handy at this time. We can operate on the event list and search for a specific ID:

function getEventById($eventId, $calendarId) {     foreach ($this->getEventsForCalendar($calendarId) as $event)        if ($event['id'] == $eventId)           return $event;  }

Then the corresponding call of the Presenter will complete the output:

function printEventDetails() {     $this->putEvent(        $this->businessLogic->getEventById(           $_GET['showThisEvent'],           $_GET['calendarId']        )     );  }

In this case, we have successfully completed the Dependency inversion.

The control flow is still directed to the Presenter by Logic, and all output content is completely defined by Logic. In this way, if we plan to access other calendar services, we only need to create another Logic class and inject it into the Presenter. The Presenter itself will not perceive any difference. In addition, the source code dependency is also put upside down. Presenter is the only class created and directly dependent on Logic. This dependency is critical to ensure that the Presenter can change the data display mode without affecting the Logic content. In addition, this dependency allows us to use CLI Presenter or any other method to display information to users to replace HTML Presenter.

Get rid of global variables

Currently, the only potential design defect for network leakage is the $ client global variable. All the code in the application will access it, but in stark contrast, it is really necessary to access $ client only one Logic class. The most intuitive solution is to change it to a dedicated class variable, but then we need to pass $ client to the Presenter through the Router, so that the presenter can use the $ client change to create a Logic object-which obviously does not play a role in solving the problem. Our original design was to create classes in an independent environment and assign dependencies to them accurately.

We tend to use Factories for any large class structure, but in this small example, the index. php file is sufficient to accommodate logic creation. As the entry point of the application, the file similar to "main" in the high-level architecture is still out of the scope of business logic.

Therefore, we change the code in index. php to the following, and keep all the content and the session_start () command:

$client = createClient();  if(!authenticate($client)) return;  $logic = new Logic($client);  $presenter = new Presenter($logic);  (new Router($presenter))->doUserAction();

Conclusion

Now the work is complete. Of course, there must be much room for improvement in our design. We can write some test procedures for methods in the Logic class. Maybe the Logic class itself can also be changed to a more representative name, such as GoogleCalendarGateway. We can also create events and Calendar classes to better control related data and behaviors, and split the Presenter dependency into arrays based on the data type. Another improvement and extension policy is to create a polymorphism behavior class to replace calling a function directly through $ _ GET. All in all, the improvements to this example are endless. Interested friends can try to turn their ideas into reality. I saved the final version of the Code in the GoogleCalObjectOrientedFinal folder of the attachment. You can start with this.

If you are curious, you can also try to connect this small application with other calendar services to see how to output information in different ways on different platforms. For friends who use NetBeans, each source code folder contains a NetBeans project. You only need to open it directly. In the final version, PHPUnit is also ready. But I removed it from other projects-because it has not been tested.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.