Chapter 10. Application Architecture

The architecture of a large web application has many facets, and we’ve already touched on many of these in previous chapters. In this chapter, we’ll focus on one of the most prominent reflections of application architecture: organizing classes and files on the server so they reinforce the modularity established in your large-scale HTML, CSS, JavaScript, and server-side scripts. We’ll also look at how this structure in a large web application helps with its maintenance down the road; you’ll see how important it is to make sure the organization you choose reflects the scope in which you expect each class or file to be used. Tenet 10 (from Chapter 1) addresses this issue:

Tenet 10: The organization of files on the server for a large web application reflects the architecture of the application itself, including clearly demarcated scopes in which each file will be used.

We’ll begin by examining the design of a sample web page in terms of the modules it contains, because thinking modularly about web pages is a key part of envisioning the architecture for a large web application overall. We’ll then go into more detail about how to organize the classes we presented in earlier chapters into files. Finally, we’ll explore how the architectural discussion in this chapter, coupled with the techniques discussed earlier in the book, help you manage certain common situations that are likely to arise while maintaining a large web application. This will demonstrate further how a modular implementation improves reusability, maintainability, and reliability in a large web application over its lifetime.

Thinking Modularly

As mentioned in Chapter 3, when you develop a large web application, you should plan on ways to reuse as many of its components as possible. Even if you cannot find much that you expect to reuse, building a page as a set of individual components, or modules, will make a page easier to maintain and more reliable.

What constitutes a good module will often be fairly obvious; at other times, certain better divisions will reveal themselves as you build things. Figure 10-1 illustrates some examples of modules that appear to be rather natural divisions of the page into functional units: a search box, an ad unit, and a module for selecting new cars, for example. These are rather intuitive divisions. On the other hand, you might find it better in the end to break the module for selecting new cars into two modules: one including the Go button and everything above it, and the other including everything below this point. This division would give you flexibility to use each of the smaller units independently elsewhere.

Some of the modules on a web page
Figure 10-1. Some of the modules on a web page

Components that implement the overall structure for a page may not seem as easy to break into modules at first, but because components for this purpose offer a good means of abstraction and have a strong potential for reuse, they can be very good modules as well. Figure 10-2 shows a layout, which is the generic template that defines the overarching structure of a page. Figure 10-3 shows a container, which is a generic grouping of modules that you can place within layouts to support common organizations.

A reusable layout for a web page
Figure 10-2. A reusable layout for a web page
A reusable container for a web page
Figure 10-3. A reusable container for a web page

It’s good to implement layouts and containers as modules because they each require their own HTML and CSS, and we shouldn’t have to duplicate this code each time we need the same structure. Designers tend to think in terms of these reusable patterns as well, so layouts and containers also provide a good opportunity for engineers and designers to work together to establish standard guidelines for the overall structure of pages. Chapters 4 and 7 show how to implement layouts and containers using HTML, CSS, and PHP.

Organizing Components

As you build up a library of pages, modules, layouts, and containers in your architecture, it’s important to look at how and where you intend to use each as you decide how to organize them. In the sections that follow, we’ll explore a directory structure that presents one way to organize the components we’ve discussed for large web applications in previous chapters as an example. A reasonable place to start for most web applications is to decide where in the architecture each component belongs. Is it sitewide, section-specific, or page-specific?

Sitewide Architecture

In most large web applications, there are many components that you’ll want to share across the entire application. These belong at a point in your directory structure that conveys this sitewide importance.

Pages and modules

The base classes for pages and modules are excellent examples of components that are used across an entire web application. By placing them in a directory called common, or something similar, at the top of the directory structure, you convey their sitewide scope. Each file is named after the class that it contains:

  • .../common/page.inc

  • .../common/sitepage.inc

  • .../common/module.inc

  • ...

You include these files whenever you implement your own classes for specific pages and modules. Example 10-1 shows NavBar derived from the Module base class, which requires module.inc.

Note

We’re not going to examine the details of many of the classes in this chapter. You can learn more about their implementations in Chapter 7.

Example 10-1. Deriving the Navigation Bar module from Module
<?php
require_once(".../common/module.inc");

class NavBar extends Module
{
   // You need to include module.inc to derive this class from Module.
   ...
}
?>

The top-level common directory is also a logical place to put classes for specific modules that you expect to use across the entire web application. Each file is named after the module class that it contains:

  • .../common/navbar.inc

  • .../common/subnav.inc

  • .../common/searchbox.inc

  • ...

Layouts and containers

Layouts and containers tend to be useful across an entire web application as well. Because layouts and containers fulfill a somewhat unique role, it may be helpful to place them in their own subdirectory, separate from the other common components. This directory contains files for specific layouts and containers as well as a file containing the Layout base class itself:

  • .../common/layout/layout.inc

  • .../common/layout/landinglayout.inc

  • .../common/layout/resultslayout.inc

  • .../common/layout/detailslayout.inc

  • .../common/layout/container4x1.inc

  • .../common/layout/container3x1.inc

  • ...

In Chapter 7, you saw that layouts actually have a lot in common with modules. As a result, Layout is derived from Module, as shown in Example 10-2, and layout.inc requires module.inc. Example 10-3 shows how specific layouts are derived from Layout; these files all require layout.inc.

Example 10-2. Deriving a layout as a module
<?php
require_once(".../common/module.inc");

class Layout extends Module
{
   // You need to include module.inc to derive this class from Module.
   ...
}
?>
Example 10-3. Deriving a specific layout
<?php
require_once(".../common/layout/layout.inc");

class ResultsLayout extends Layout
{
   // You need to include layout.inc to derive this class from Layout.
   ...
}
?>

CSS and JavaScript

In Chapter 9, you saw that a good starting point for managing the organization of CSS and JavaScript files in a large web application is a common file containing CSS or JavaScript applicable to most parts of the site, a set of sectional files each specific to one section of the site, and as few other CSS or JavaScript files as possible. Sitewide CSS and JavaScript files go in their own top-level directories:

  • .../css/sitewide_20091011.css

  • .../css/printers_20091011.css

  • .../js/sitewide_20090929.js

  • ...

Data management

In Chapter 6, you saw that data managers provide a clearly defined interface between the user interface and backend systems. We also discussed the fact that it is helpful to define data managers for fairly granular sets of data grouped logically from the backend perspective. After you have defined the data managers, the user interface can instantiate whichever of them are needed to load and save specific sets of data.

Because data managers are organized from the backend perspective, it’s good to define a data manager directory under which you can create whichever subdirectories best reflect the backend architecture for data:

  • .../datamgr

Images and other resources

Images and other types of resources (PDF files, Flash files, etc.) used across the entire web application deserve dedicated areas, too:

  • .../images

  • .../docs

  • .../media

  • ...

Recall from Chapter 9 that if you employ the techniques for spriting images, you’ll end up with fewer image files to organize under the images directory.

Section Architecture

Most large web applications have some components that are specific to certain sections of the application, so it makes sense to place them at a point in the directory structure that reflects this. It’s reasonable to make the directory structure for sections similar to the one for sitewide components.

Pages specific to one section

Pages within a certain section of a large web application often require similar capabilities that you can implement in a base class. By placing them in a directory called common, or something similar, at the top of the directory structure for the section, you convey their applicability to the entire section instead of the entire application. Each file is named after the class that it contains (e.g., “cpo” indicates Certified Preowned):

  • .../newcars/common/nwcpage.inc

  • .../cpocars/common/cpopage.inc

  • ...

You include these files whenever you implement your own classes for more specific pages. Example 10-4 shows NewCarDetailsPage derived from the NewCarsPage base class, which requires nwcpage.inc.

Example 10-4. The New Car Details page within the New Cars section
<?php
require_once(".../newcars/common/nwcpage.inc");
...

class NewCarDetailsPage extends NewCarsPage
{
   // You need to include nwcpage.inc to derive this from NewCarsPage.
   ...
}
?>

A section-specific common directory is also a logical place to put classes for specific modules that you expect to use throughout that section. Each file is named after the module class that it contains:

  • .../newcars/common/nwcheader.inc

  • .../newcars/common/nwccallout.inc

  • ...

Other types of section files

Just as we defined a number of subdirectories for various other components used across the entire application, you can define similar directories for those components that are specific to a certain section:

  • .../newcars/css

  • .../newcars/js

  • .../newcars/images

  • .../newcars/docs

  • .../newcars/media

  • ...

An alternative to placing CSS and JavaScript files for the section within the sections themselves is to place them within the sitewide directory for CSS or JavaScript and give them a section name:

  • .../css/newcars_20091009.css

  • .../css/cpocars_20091013.css

Architecture for Pages

The most specific points in the architecture address individual pages. As you navigate around a web application, a natural hierarchy among pages usually becomes evident. For example, at the highest level, there may be a series of landing pages that take you into sections of the application:

  • .../homepage

  • .../newcars

  • .../cpocars

  • .../usedcars

  • ...

Once you navigate into these sections, there are often ways from those points to move deeper within each section. For example, within the New Cars section, you might have a page for new car search results:

  • .../newcars/search

When you select one of the results, it’s likely that you’ll need a page for displaying the details about that specific car:

  • .../newcars/details

Setting up a hierarchy like this for the directory structure on your server is a good reflection of the application architecture. These directories may be what you end up using for paths in the URLs to reach various parts of your web application, or you may end up creating rewrite rules on the server (to address the latest demands of the various search engines, for example). Even if you do end up creating rewrite rules, a hierarchy for pages provides solid underpinnings with which to work.

Files for implementing pages

In Chapter 7, you saw how to implement web pages as PHP classes that construct pages as sets of modules. To do this, you need only two files in the directory for each page: index.html, which instantiates the class for a specific page (see Example 10-5), and index_main.inc (see Example 10-6), which defines the page class itself.

Example 10-5. The index.html file for a page
<?php
require_once(".../homepage/index_main.inc");

$page = new Homepage(...);
$body = $page->create();

print($page->get_page());
?>
Example 10-6. The index_main.inc file for a page
<?php
// Include whatever files are needed for the page (e.g., usually files
// for the page base class, data managers, and all modules on the page.)
...

class Homepage extends SitePage
{
   ...
}
?>

Page-specific modules

In Chapter 7, you saw that everything a module needs is nicely encapsulated within a class as well. For modules used by just one page, the index_main.inc file for the page is a good place to define the module class. This reflects the fact that the scope for the module is just that page. In Example 10-7, the class for the Car Finder module is defined within the same file as the class for the home page.

Example 10-7. The home page, using a module specific to the home page
<?php
...

class Homepage extends SitePage
{
   // This page is the only one that uses the Car Finder module below.
   ...
}

class CarFinder extends Module
{
   ...
}
?>

Modules on multiple pages

For modules used on multiple pages, define the module class in its own file (within one of the common locations discussed previously), and include the file wherever you use the module. In Example 10-8, the class for the Navigation Bar module is defined in its own file, named after the module itself (e.g., navbar.inc). The index_main.inc file for the home page includes navbar.inc.

Example 10-8. The home page using a module used by other pages, too
<?php
require_once(".../common/navbar.inc");
...

class Homepage extends SitePage
{
   // This page uses the Navigation Bar module defined in navbar.inc.
   ...
}
?>

Recall from Chapter 7 that each module has exactly one include file that you include to use it. If the module needs other files for its implementation, the module includes them itself. This is key to good encapsulation.

Pages of a similar type

Pages of a similar type that require similar capabilities may be derived from a common base class. In Example 10-9, the Homepage class is derived from LandingPage instead of the SitePage page class because it is one type of landing page and needs some of the same capabilities as others. LandingPage, in turn, is derived from SitePage (not shown here).

Example 10-9. The home page inheriting capabilities of a landing page
<?php
require_once(".../common/landingpage.inc");

class Homepage extends LandingPage
{
   // You need to include landingpage.inc to inherit from LandingPage.
   ...
}
?>

Architecture and Maintenance

Now that we have looked at a few ideas for organizing various components of a large web application, let’s look at how a good architecture coupled with the techniques presented earlier in the book help address some common issues related to maintenance. In the end, it’s an application’s capability of adapting to changes that makes it truly maintainable.

Reorganizing Module Uses

One of the most common maintenance issues with large web applications is keeping the architecture organized as components are moved from one place to another. For example, suppose a module originally designed specifically for one page later is used on several pages across the entire application. Fortunately, with the architecture described earlier, the change is relatively easy.

Let’s look at a module called NewCarCallout, originally designed for the New Car Details Page. Here are the steps to start using it across the entire application while keeping the architecture well organized:

  1. Move the class NewCarCallout from its location within the index_main.inc file for NewCarDetailsPage to its own file, .../common/nwccallout.inc.

  2. Add a require_once statement to include .../common/nwccallout.inc in the file index_main.inc for NewCarDetailsPage.

  3. Add the module to whichever pages will use it by performing Step 2 for those pages and instantiating the module on those pages wherever the module should be added.

One of the most important things to notice about this process is that you don’t have to move any CSS or JavaScript to use the module at its new location. Recall from Chapter 7 that links to CSS and JavaScript, as well as embedded CSS and JavaScript, are encapsulated by the class itself; the CSS and JavaScript effectively travel around with the module wherever it goes.

Example 10-10 is a reminder of the methods outlined in Chapter 7 that you might define for a module. As you can see, in addition to the get_content method, which returns the HTML for the module, there are methods to specify the linked and embedded CSS, and the linked and embedded JavaScript.

Example 10-10. A module specifying its CSS, JavaScript, and HTML
class NewCarCallout extends Module
{
   ...

   public function __construct(...)
   {
      // Do whatever is required to initialize the module instance.
      ...
   }

   public function get_css_linked()
   {
      // Return an array of CSS files you want linked for the module.
      return array
      (
         ...
      );
   }

   public function get_css()
   {
      // Return the string of CSS you want embedded for the module.
      return <<EOD
...
EOD;
   }

   public function get_js_linked()
   {
      // Return an array of JavaScript files that you wanted linked.
      return array
      (
         ...
      );
   }

   public function get_js()
   {
      // Return the JavaScript that you want embedded for the module.
      return <<<EOD
...
EOD;
   }

   public function get_content()
   {
      // Return the HTML markup to place on the page for the module.
      return <<<EOD
...
EOD;
   }
}

By defining the methods in Example 10-10, the module will work properly wherever it goes; however, it would be good to refactor its CSS and JavaScript into the sitewide files to reflect its new, more universal role in the architecture. This refactoring is easy to do because the CSS and JavaScript methods for a module also document exactly where the code to be refactored resides. In addition, the ID for the module’s outermost div identifies exactly which block of CSS needs to move (see Chapter 4), and the namespace for the module’s JavaScript identifies exactly what to change in the JavaScript (see Chapter 5).

It’s worth noting that if a module that was used across an entire web application is reduced to being used on just one page or just one section, it’s probably not worth moving the module from the common area to the more specific area, since the module has already demonstrated its relevance at a wider scope. However, even if you decide to make that change, it’s a simple one since you will have already removed the module from where it was previously used, and you just need to refactor the CSS and JavaScript in the reverse direction.

Adding Module Variations

The more places you use a module within a large web application, the more likely you’ll need different variations of it. When these variations require coordinated changes to a module’s HTML, CSS, and JavaScript, they can become difficult to manage. Fortunately, the architecture we’ve described for modules makes handling such variations much easier. Because everything a module needs to function correctly is encapsulated inside the module class itself, you have a complete picture of what needs to change to support future variations.

Often, variations will require new parameters to configure the module, but you want to avoid altering code where the module is used already. There are a couple of ways to handle this: you can define additional parameters for the constructor of the module, and adapt the constructor to treat the parameters as optional; or you can define setter methods to handle the additional parameters after construction.

Let’s revisit the Picture Slider module from Chapter 7 to examine how both of these alternatives work. Example 10-11 shows a small part of the implementation provided there. Suppose in a new variation of the module you want to allow the width of the pictures and the number of frames to be configured wherever the module is used. In Chapter 7, these were fixed at 65 and 8, respectively.

Example 10-11. The original implementation for the Picture Slider module
class PictureSlider extends Module
{
   var $gallery;
   var $type;
   var $picture_width;
   var $slider_frames;

   public function __construct($page, $gallery)
   {
      parent::__construct($page);

      $this->gallery = $gallery;
      $this->type = "default";
      $this->picture_width = 65;
      $this->slider_frames = 8;
   }

   ...
}

Again, the key is that you don’t want to alter code at any of the points where the module is already being used. This is how existing instances of the module are created:

$mod = new PictureSlider($this, $gallery);
$slider = $mod->create();

Example 10-12 shows how you could use optional parameters within the constructor to support the new variation of the module.

Example 10-12. The Picture Slider module with a new constructor
class PictureSlider extends Module
{
   var $gallery;
   var $type;
   var $picture_width;
   var $slider_frames;

   public function __construct($page, $gallery, $width="", $frames="")
   {
      parent::__construct($page);

      $this->gallery = $gallery;
      $this->type = "default";

      if (empty($width))
         $this->picture_width = 65;
      else
         $this->picture_width = $width;

      if (empty($frames))
         $this->slider_frames = 8;
      else
         $this->slider_frames = $frames;
   }

   ...
}

With this change, existing instances of the module work just as they did before; however, now you can also do the following:

$mod = new PictureSlider($this, $gallery, $width, $frames);
$slider = $mod->create();

Example 10-13 shows how you can apply the alternative approach of defining setter methods to set optional parameters for the module.

Example 10-13. The Picture Slider module with setter methods
class PictureSlider extends Module
{
   var $gallery;
   var $type;
   var $picture_width;
   var $slider_frames;

   public function __construct($page, $gallery)
   {
      parent::__construct($page);

      $this->gallery = $gallery;
      $this->type = "default";
      $this->picture_width = 65;
      $this->slider_frames = 8;
   }

   public function set_picture_width($width)
   {
      $this->picture_width = $width;
   }

   public function set_slider_frames($frames)
   {
      $this->slider_frames = $frames;
   }

   ...
}

With this change, existing instances of the module work just as they did before; however, now you can also do the following:

$mod = new PictureSlider($this, $gallery);
$mod->set_picture_width($width);
$mod->set_slider_frames($frames);
$slider = $mod->create();

Another common variation for modules is simply to affect a presentation change based on settings within the module. By changing these settings, either with a parameter passed to the constructor or via a setter method, you can apply different CSS classes to achieve different presentations using presentation switching. Chapter 4 discusses presentation switching in detail.

Making Widespread Changes

Over the lifetime of a large web application, it can become more and more difficult to make widespread changes so that they are consistent wherever they are applied. However, with a little forethought in your architecture, these changes don’t have to be difficult to manage.

Base classes for pages and modules provide a good place to address this situation. As an example, let’s suppose after some period of time your design team decides that you need a different header and footer on all the pages within a certain section of your web application. Recall from Chapter 7 that Page prescribes an interface for returning the HTML markup for the header and footer of pages. Normally SitePage, the base class for all pages across an entire web application, defines both of these methods, as shown in Example 10-14.

Example 10-14. Defining a header and footer for an entire site
class SitePage extends Page
{
   ...

   public function get_header()
   {
      // Return the HTML markup for the header across the entire site.
      return <<<EOD
<div id="sitehdr">
   ...
</div>

EOD;
   }

   public function get_footer()
   {
      // Return the HTML markup for the footer across the entire site.
      return <<<EOD
<div id="siteftr">
   ...
</div>

EOD;
   }

   ...
}

To change the header and footer within a certain section, define a page class from which to derive all pages for just that section and override the get_header and get_footer methods to return the section-specific header and footer, as shown in Example 10-15.

Example 10-15. Defining a special header and footer for the New Cars section
class NewCarsPage extends SitePage
{
   ...

   public function get_header()
   {
      // Return the HTML markup for the header of the New Cars section.
      return <<<EOD
<div id="nwchdr">
   ...
</div>

EOD;
   }

   public function get_footer()
   {
      // Return the HTML markup for the footer of the New Cars section.
      return <<<EOD
<div id="nwcftr">
   ...
</div>

EOD;
   }

   ...
}

How can you be sure the desired header and footer will be retrieved from the new definitions just created for NewCarsPage and not from SitePage? The correct methods are used because in the create method of the base class Page, $this now points to an instance of NewCarsPage. So, the get_header and get_footer methods of this class are invoked, as opposed to the methods in SitePage. Example 10-16 shows where this occurs in Page.

Example 10-16. Invoking get_header and get_footer within the Page class
class Page
{
   ...

   public function create()
   {

      ...

      // These invoke methods of the derived class, if defined there.
      $header = $this->get_header();
      $content = $this->get_content();
      $footer = $this->get_footer();

      ...
   }

   ...
}

If the header or footer defined in the section-specific page class requires CSS or JavaScript itself, you can place it in the CSS file or JavaScript file defined for that section of the site (if you’re following the recommendations of Chapter 9). Page classes define an interface for specifying linked and embedded CSS and JavaScript exactly like that for modules. This way, whenever you add HTML within the page class, you can specify the CSS and JavaScript explicitly that you need.

The ideas mentioned in this section are very extensible. For example, you can define your own methods in a base class derived from Page and override these methods as needed. Another way to apply widespread changes is to make the changes directly within modules that are used in multiple places across your web application. Modules used in many places themselves inherently offer a central point for making widespread changes.

Changes in Data Sources

When the source behind a set of data changes, ideally the change should be abstracted from the user interface, provided the structure of the data is still the same. This demonstrates the key benefit of using data managers to establish a clearly defined interface between the user interface and backend systems. As mentioned in Chapter 6, the interface established by a data manager forms a contract of sorts between the user interface and the backend that prescribes the structure of the data to be exchanged. Example 10-17 illustrates this. As long as both parties work within the guidelines of the data structure, the source of the data can change however it needs to.

Example 10-17. The structure, or contract, for data managed via a data manager
array
(
   "0" => array
   (
      "name"  => "...",
      "price" => "...",
      "link"  => "..."
   ),

   "1" => array
   (
      "name"  => "...",
      "price" => "...",
      "link"  => "..."
   ),

   "2" => array
   (
      "name"  => "...",
      "price" => "...",
      "link"  => "..."
   ),

   ...
)

One example of a data source changing is when your application gets a new data provider for one of its services. For example, suppose your web application is going to start using a new company to provide periodic updates to new car inventories by way of a data feed. Changes to the user interface code are needed no further than in the get_data methods of the data managers that fetch data from the feed.

Another interesting example of a data source changing occurs when you have a content management system (CMS) managing some data. Imagine a control panel through which product managers can control which content will be retrieved by the backend at various times. This may be dynamic data for modules to display, or it might even be metadata that controls whether a module is displayed at all.

In a content management system, it’s usually important to be able to preview how parts of the web application will look at scheduled times. To support previews, you can define a data manager base class that stores a time parameter with the other input arguments passed to a data manager. To set the preview time, look for a special value passed via GET or POST, or using a cookie (all retrieved in PHP via the $_REQUEST variable). Examples 10-18 and 10-19 illustrate handling previews for time-sensitive CMS data. The important thing to notice is that changes within the data source based on the preview time are abstracted from the user interface.

Note

Previewing is typically available in environments other than production systems, so that’s how Examples 10-18 and 10-19 are implemented. In practice, the backend systems accessed for previews are typically different from those in the production environment as well to keep the data within the two environments isolated.

Example 10-18. A base class for data managers that access CMS data
class CMSDataManager extends DataManager
{
   protected $op_environment;
   protected $cms_preview_tm;

   public function __construct()
   {
      parent::__construct();

      // Set this from a server config that indicates the environment.
      $this->op_environment = ...;

      if ($this->op_environment != "production" && !empty($_REQUEST
         ["cmstime"]))
      {
         // Set the timestamp to use for the content management system.
         $this->cms_preview_tm = $_REQUEST["cmstime"];
      }
      else
      {
         // In production, or if no preview time was given, use the
         // current time as the time for which data is to be fetched.
         $this->cms_preview_tm = time();
      }
   }

   // No implementations are necessary for get_data and set_data here.
}
Example 10-19. A specific data manager accessing CMS data
class NewCarListingsDataManager extends CMSDataManager
{
   public function __construct()
   {
      parent::__construct();

      ...
   }

   public function get_data($load_args, &$load_data, &$load_stat)
   {
      // Explicitly add the timestamp for the content management system.
      $load_args["cms_preview_tm"] = $this->cms_preview_tm;

      // Do whatever else you would normally do to retrieve the data.
      // The timestamp is passed automatically with the other arguments
      // so that it can be used as needed to fetch time-sensitive data.
      ...
   }

   ...
}

Exposing Modules Externally

Clearly, the classes presented in Chapter 7 are designed to work together, but an important attribute is that they can also work independently. The same interface that you use to create modules on pages derived from Page lets you extract everything a module needs (e.g., its HTML, CSS, and JavaScript) so that it can be placed on pages generated within a different environment, as shown in Example 10-20. Modules do need some instance of Page passed to the constructor, but you can ignore the page after you construct it and pass it to the modules. For this, you may want to define a special page class (e.g., TransPage) with empty implementations for methods that modules call but that aren’t needed in this context. For example, modules call add_to_js_linked, add_to_js, add_to_css_linked, and add_to_css, but it’s more efficient to have these do nothing if the page instance is just a placeholder.

Example 10-20. Accessing the components of a module
// You do need some instance of Page for modules, but then ignore it.
$page = new TransPage();

...

// Anyone can extract a module's HTML, CSS, and JavaScript like this.
$mod = new PictureSlider($page, ...);
$html = $mod->create();

$css_linked = $mod->get_css_linked();
$css = $mod->get_css();

$js_linked = $mod->get_js_linked();
$js = $mod->get_js();

The important thing here is that this lets you build systems in an evolutionary way. Large web applications take a lot of time to build and modify, so it’s important to have a truly modular architecture that has the potential to work within other systems while different parts are in different states.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset