nihaogirl 发表于 2017-4-8 08:06:17

转载 PHP Application Framework Design: 1 – Getting Started

  转自   http://www.lbsharp.com/wordpress/index.php/2005/10/13/php-application-framework-design-1-getting-started/
This articledescribes the design of a complete application framework written in PHP. Thereader is assumed to have a working knowledge of PHP. This part of the seriesdescribes the scope of the framework and goes over the initial class hierarchy.The following parts cover everything from session handling to creating pagetemplates.
Introduction
I started webapplication development back in the days of ASP 3.0. Back then, ASP was sodeficient that we had to write most of the code in DLLs and include them intothe web projects. Sure, it was a bit cumbersome, but IIS was the best platformavailable for getting a team of VB developers productive. Then ASP.NET camearound and fixed many of its predecessors deficiencies so the company where Iworked at the time jumped on the bandwagon. Although I didn’t realize it at thetime, .NET spoiled me with its ease of developing large applications (withoutmuch need for thorough design and planning).
Don’t get me wrongthis is not an advertisement for Microsoft. While .NET was gaining ground overthe Java platform (please don’t email me saying how Java is better thats notthe point here), there were quite a few developments in the open sourcecommunity as well. Apache has long established itself as the leading web serversoftware and PHP became the open-source answer to ASP. By avoiding the overheadof CGI1, PHPcombines speed and efficiency with the power and style of Perl. PHP alsohappens to be cross-platform and free (as in beer2) which makesit a no-brainer solution for many projects.
Unfortunately, PHPhas its limitations. Although PHP does fix many features that make Perlunreadable, using it for large projects can be somewhat difficult (relative toJava or .NET that is). So what are the possible problems? For starters, largeprojects involve many developers with wide ranges of skill-sets. For web-projects,you may have people who don’t even know how to do anything save design pagelayouts and make pretty pictures (and yes we appreciate you guys as well). Howdo you get everyone to work together productively and be consistent? You maysay that this is the Team Leaders job but managers can use help too sometimes.Developing in PHP can also be somewhat tedious when you find yourselfcopy-pasting the same blocks of code (e.g. to authenticate the user, insert thepage template, connect to the database etc). This article will try to resolvethese issues.
So whats missingfrom PHP? Well, nothing actually, if you spend some time to design what youwant to accomplish. While platforms like .NET force you into doing things theMicrosoft way, PHP gives you the flexibility to do things your way. Use thispower wisely or else you will see the performance of your PHP applicationdwindle down to that of .NET or even Java (heaven forbid). And, while we are onthe subject, why not just use .NET since it does so much work for you? Thatquestion is left up to the reader. Maybe the choice of using PHP was alreadymade for you (by your management by magazine type boss). Maybe PHP is the onlytool installed on your server. Or maybe you just like the syntax. I reallydon’t care and, if you are reading this (as opposed to having this read toyou), the choice was probably already made. I also realize that there are somecommercial products that provide a canned-framework for you to use that solveall the problems that I described above. I’m not trying to sell you anythingjust trying to show that you can easily come up with your own framework. Letsget cracking shall we.
Class Hierarchy
I like to startimplementing large projects by drawing out the class diagrams and seeing how thecomponents fit together. Luckily, as of version 4, PHP supports object orientedprogramming with all its bells and whistles. Although this article is writtenfor PHP 4, you can accomplish all of the same results with PHP 5 (which isstill somewhat bleeding edge at the time of writing this). Object orientedprogramming and design is beyond the scope of this article so I hope you are atleast somewhat familiar with it. The point is that OOP is the key to developinglarge applications and web-development is no exception.
Update: PHP 5 is now fairlymainstream. Although the framework, as described, will still work, some of thehacks described in this article are no longer needed.
We will want tokeep the framework minimal for performance reasons yet scalable and flexibleenough to handle any kind of project that you may encounter. To start of, letsagree that the logic for each and every page of you application will beencapsulated in a class. This class will inherit from the system base class,which will handle everything that you may want your application to do. Thebenefits of this approach are fairly obvious. First, you are encapsulating allof the complex code away from the project developers. Thus, you can restassured that your new intern will not mess with your system classes (unless youwant her to for some reason). All the pages in the application will be moreconsistent and you will not be copy-pasting the same code into every page. Moreimportantly, if you decide to change some underlying functions of yourapplication, you will not have to edit each page as long as you keep theinterface to your system base class consistent.
Now lets definewhat we want our system base class to do. Here is a basic list of features thatI will go over in due time:

[*]Establish the database connection
[*]Define how the page will be laid out and printthe HTML
[*]Manage authentication and user sessioninformation
[*]Define all your applications core features
Here is theframework and all of it’s glory:
class_system.php     - The base class which includes thefollowing
|_constants.php      - Application constants
|_functions.php      - Generic functions
|_class_user.php     - Session handling
|_class_template.php -Template handling
|_class_form.php     - Form handling
Using thisframework, all of your pages should have this structure:
include"include/class_system.php";
class Page extendsSystemBase {
    //your code here
}
$p = new Page();
The idea here isthat we want to minimize the time spent on the most common tasks such ascreating new pages. In this case, all we have to do is include the base class(which will make sure that all the other necessary dependencies are alsoincluded), extend the base class, and simply make an instance of the class toget the ball rolling. This way you can start thinking about what the page willactually do rather than waste time with common tasks.
Configuration Filesand Useful Functions
The simplest ideato explain is the need for a file that will contain the applicationsconfiguration. We can go with XML or some other even more intricate technologythat will inevitably make the framework less efficient but lets start off withthe basics. All we really need is a set of constants that define strings suchas the database connection or installation paths. These are strings that wewould not want to hunt down within each page of the application every time thedatabase password is changed or the application is moved. The file constants.php is just that. The settings that I find most useful are the URL and systempaths to the application.
On a similar note,there are some functions that we would like to always make available and Iinclude them in the file functions.php. Thesefunctions are not made as methods of the base class because we would like tomake them available to all the classes of our application (including those thatdo not extend the base class). I will go over the functions in this file whenwe need to use them. As a simple example, I include the me() function, which returns the file name of the current PHP file. (VB 6programmers will remember that me is a reference to the current form).
function me() {
    return substr($_SERVER['PHP_SELF'],strrpos($_SERVER['PHP_SELF'],'/')+1,
                  strlen($_SERVER['PHP_SELF']));
}
The Base Class
Now lets dissectthe class_system.php file line by line. The first thing that we do is call session_start() which starts a new user session if none exists or does nothing otherwise(more on sessions in part 2 of this series). Then we include all the needlibraries. There are two things to note about the way this is done. First,require_once is used which will only include the file once and throw an exception ifthe file is not available (as opposed to include which would only make a warning and proceed with execution of the page).The second thing to note is that absolute paths are used to include the fileswhich are all in the same folder. This is done because we don’t want to have toreconfigure the server to get the application working (by changing the includepath used in PHP). Relative paths would not work because the base class will beincluded from various other pages and the relative path would have to work foreach page that includes the base class.
session_start();
$path =dirname(__FILE__);
require_once"$path/constants.php";    //defines
require_once"$path/functions.php";    //generic functions
require_once"$path/class_user.php";   //session handling
require_once"$path/class_template.php";//template handling
require_once"$path/class_form.php";   //form handling
The implementationof the system base class is not at all surprising if you consider the way we intendedto use it. Since the code is only executed when an instance of the class iscreated, we put everything that we want to happen into the class constructor.Also, since the base class will not be able to know exactly how to render eachpage, we make the methods abstract and let polymorphism take care of the rest(i.e. the derived classes will override the base classes methods.) Here is alist of the methods called from within the constructor:
init()              - initialize the page
authenticate()      -perform authentication
handleFormEvents()  - handle page submits
printPage()         - output the HTML
destroy()           - destroy page (close DB connection)
Unfortunately, PHP4 does not enforce many OOP principles (note that PHP 5 has a new and much morerobust object model). You can get everything to work, such as inheritance andpolymorphism, but it takes some effort and some faith. For example, there is noconcept of method protection so all methods (and attributes for that matter)are made public. This is a big no-no when it comes to OOP so a commonconvention is to prefix all methods that are intended to be private with anunderscore ( _ ) and then take it on faith that the users of the class will notcall these methods. Another problem is that we cannot declare the class to beabstract (i.e. we do not want people to declare instances of the base class butrather force them to inherit from it). We can get around this limitation by
including the following lines at the top of our constructor (you can read this article for an in-depth analysis of how this works). The code checks tosee if the instance of the class is the base class and throws an exception.
if(!is_subclass_of($this,'SystemBase')) {
    trigger_error('Base instantiation from nonsubclass',E_USER_ERROR);
    return NULL;
}
Database Access
The applicationframework should provide a clean interface to your data regardless of the typeof SQL server uses. It is also very desirable to have loose coupling betweenyour application and your database backend. So, for example, if you ever decideto change the location of the database or even change the type of SQL serverused, you dont want to have to rewrite all your code. Luckily, someone hasalready tackled this problem for us and all we have to do is use the PEAR::DBmodule, which provides a consistent interface to several popular databaseservers. You can read all about it at the PHP Extension and Application Repository and I recommend reading the following PEAR::DB tutorial as well.
Update: The new preferred databaselibrary is PEAR::MDB2 which is a merge ofPEAR::DB and Metabase libraries. You will find migrating from DB to MDB2 fairlystraight forward. There is even a migration guide.
Assuming that thePEAR::DB module is installed and configured correctly, all we need to have apersistent database connection available in every page is a reference to aPEAR::DB object. We will create this object as a member variable in the SystemBase class and create the database connection inside the constructor. Wealready have the necessary constants to build a DNS string and there is also afunction in functions.php that returns a pointer to a new PEAR::DB object.
function&db_connect() {
    require_once 'DB.php';  //pear db class
    PEAR::setErrorHandling(PEAR_ERROR_DIE);
 
    $db_host = DB_HOST;
    $db_user = DB_USER;
    $db_pass = DB_PASS;
    $db_name = DB_NAME;
 
    $dsn ="mysql://$db_user:$db_pass@$db_host/$db_name";
    $db = DB::connect($dsn);
    $db->setFetchMode(DB_FETCHMODE_ASSOC);
    return $db;
}
The functionimports the PEAR:DB library if it has not yet been imported, creates a DNSstring, and connects to the database. All we have to do now is use thisfunction in the SystemBase constructor as follows: $this->db =db_connect(); and we are done.
Optimization
You may think itis a bit too early to discuss code optimization at this point since we have yetto implement most of the necessary features. Code optimization is more of anart and I will not go into a line-by-line analyses of how we can achieve thesame effect faster. The point here is that we already can tell what the majorbottlenecks will be so lets nip the problem now before its gets out
of hand. The most obvious performance loss will be due to the persistentdatabase connection that we establish for each page. In fact, if you make a fewtiming test to benchmark the performance of the framework you will see thatestablishing the connection alone take more time than everything else combined.Although it is a necessary evil and we will have to pay that price for it whenusing the database, not every page in our application will have to connect tothe database. In fact, not every page will need to print out HTML content (i.e.it may just process the request and redirect). So before we go on lets define afew constants for the base class to indicate that we don’t need a databaseconnection or to print the page. You will simply define these constants inpages that need them.
    define('NO_DB', 1)    if persistent DB connection is not needed
    define('NO_PRINT', 1) if page does not getrendered
Summary
So far, we havelaid the foundation for how our application will behave and how the frameworkwill be used. We established an OOP design for the application, defined someconstants and function to expedite routine work, and added database support.Read the next part to see how to manage session data and users.
1.    CGI (Common Gateway Interface) was a popular method for using server side code to generate dynamic webpages. Any programming language can be used for CGI as long as it can readsystem environment variables that are set by the web server to provideinformation about the page request. Perl has been the language of choice for CGIprogramming because CGI programs involve a lot of text processing.Unfortunately, CGI does not scale well because each page request requires theserver to fork a new process to run the CGI program. mod_perl is a way toreduce the overhead by including Perl as an internal Apache module; however,benchmarks show that PHP (which also runs as an Apache module) is still faster.
2.    The term “free as in beer” is used in theopen-source community to distinguish things that cost nothing and peoplesrights. The term was originally published in this paper.

 
页: [1]
查看完整版本: 转载 PHP Application Framework Design: 1 – Getting Started