0

I have an object oriented framework that uses a page design, where each page extends a view of the framework. So, for instance, my index page for a site is actually a class that implements the abstract class View provided by the framework. I hook the framework by including a startup script at the top of the page and after some processing the framework creates an instance of the page and processes its view data. To add flexibility to the system, I don't require the class name to be the name of the file itself. This allows me to create something like a support class and have it behave as the index for the /support/ subdomain.

I was initially passing the page's class name into the framework via the framework's constructor, but this added a few more steps to the top or bottom of the page. I currently obtain the class name via a pages table in the database identified by a filtered requested URI. I use this table to build navigation and adding an extra table column was practically free. This works OK as long as I have a database connection, but I'm trying to implement static page support for status messages and error reporting, and I need another method for creating an instance of the page's class. If I use the standard convention of naming the class the file's name, then I know I have a dependable way of extrapolating the class name. I don't really want to name all my classes index just for presentation reasons. What is some advice or some standards for how object oriented frameworks are initialized?

View.inc

<?php
abstract class View
{
    abstract function getView();
}
?>

Startup.inc

<?php
    require_once("View.inc");
    require_once("CoreController.inc");

    $Framework = new CoreController();
    $Framework->Initialize();
    exit;
?>

index.php

<?php
    require_once("Startup.inc");

    class Index extends View
    {
        public function getView()
        {
            echo "<pre>View Data</pre>";
        }
    }
?>

Within my framework I have a TemplateController that processes the page. The page class is known because it is mapped via another class. Without a database however, I would like to discover the class within, say, index.php without changing the way it's currently set up. Here is the actual code within the TemplateController.

//Get the View Class from Page
$view = $page->getPageClass();

//We need to catch View creation failure
$this->Page = new $view($this->Framework);

//Initialize the Page View
$this->Page->Initialize();

//Cache the Page View
$this->cacheView($this->Page, $page->getPageName(), $this->SiteID.TCS_PAGE_SORTID);

In the snippet above $view is the actual name of the class that was mapped from another controller. I'm passing a reference to the framework to the view's constructor and the rest is really irrelevant. I'm trying to come up with a similar mapping technique to identify the page class in the event the database is down. I like the one include line for the framework startup and would like to keep it that simple.

At the top of the TemplateController I have to also reinclude the actual page, since my startup script is at the top, the actual class is not included even though it is the requested page.

include($_SERVER['SCRIPT_FILENAME']);

Please review Stack Overflow question Identifying class names from $_SERVER['SCRIPT_FILENAME'] for more information on what I'm attempting to do.

Community
  • 1
  • 1
daganh
  • 421
  • 2
  • 5
  • Just to clarify, my framework maps the Index class to the page's uri. Without this mapping the Index class is unknown. In Index.php the class does not have to be named Index when it's mapped. I expected answers like: define("PAGECLASS", "Index"); at the top of the Page or just pass the name via the framework's constructor as done previously. – daganh Apr 20 '11 at 03:46
  • Some clarifications needed. Do you need to create instance of particular class which is responsible for the content of requested page? The problem is you don't know ahead of time which subclass of `View` you will use, right? Also, did you post full contents of `index.php`? Where does framework call `getView()`? – galymzhan Apr 20 '11 at 04:26
  • Your right: I don't know ahead of time which subclass of View will be used. I don't really want to know the name of the subclass either, but generically load the requested page's class, which is a subclass of View. The getView() method is called in the cacheView() method of the TemplateController. It simply does some output buffering and caches the output for a template. – daganh Apr 20 '11 at 04:57

4 Answers4

3

Here is my checklist of page routing:

  • Adding new page must be quick
  • You must not be able to add page unintentionally
  • Application with million pages should not be slower or more bloated than application with 2 pages
  • Provide simple way and let people enhance it if they want
  • Allow easy re-factoring, such as moving several pages into different location
  • Make framework detect everything. Don't force user to specify base URL, etc.
  • Create simple to understand page names (hello/world).
  • Clean illegal characters from the URL
  • Make it possible to add static pages easy
  • Provide a way to generate URLs from page names.
  • Allow user to use pretty URLs by changing what appears before and after the page in the URL

And most importantly - Stop copying, think about the best approach.

All of those suggestions I have used in the PHP UI framework - Agile Toolkit where I am a contributor.

Relevant sources: Agile Toolkit - a PHP Framework with jQuery, Agile Toolkit - PageManager.php, Agile Toolkit - URL.php and Agile Toolkit - PathFinder.php.

We wanted to make it really simple for developers. And secure too. And flexible. Therefore the "page" class is selected based on the URL. How? Quite easy:

/hello.html -> page_hello /hello/world.html -> page_hello_world

Class then is mapped into filename. page/hello/world.php

Then there are cases when we want to use dynamic URLs too, such as: /article/234

Many frameworks implement a complex techniques here (arrays, regular expressions, front-controllers). We don't. There is mod_rewrite for this. Just rewrite this into page=article&id=234 and it works. And scales.

Apart from pages, there are other classes, but those classes can't be accessed directly. Therefore pages live under the /page/ folder, do distinguish them and maintain security.

Then comes the designer saying - "I won't write PHP". So we introduce static pages. If class page_hello_world is not defined, we'll look into template/skin/page/hello/world.html. That's a easy shortcut for designers and non-developers to add a new page.

What if a page is not found? Api->pageNotFound() is called. Feel free to redefine and load pages from SQL or display 404 page with a picture of a pink elephant.

And then there are some pages which come from add-ons. We don't want to enable them by default, but instead let users add them to their app somewhere. How?

class page_hello_world extends Page_MegaPage

Next question, is how we handle sub-pages of that page, since sometimes all the functionality can't fit on single page. Well, let's add support for page_edit() method (or any page_XX) as an alias for a sub-page inside those classes. Now add-on developer can include a multifunctional page which can be extended, customized and placed anywhere in the application.

Finally there are hackers, who want to do something really fast. For them we apply same technique to the API. You can define function page_hello_world in the API, and you don't need class.

Some might say that there are too many ways to do a simple thing. Oh well.

I hope that this was helpful.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
romaninsh
  • 10,606
  • 4
  • 50
  • 70
  • This is exactly the type of insight I was hoping to get. It's so hard to formulate a design question if your not sure the scope of the problem in the first place. In the quest to roll my own framework, I have touched on several areas of your answer. Thanks for openning the door and showing me another way Page based designs can be implemented. The Class mapping to filename seems so obvious now. Some times all it takes is just another perspective. I will be studying your answer for some time. Thanks! – daganh Apr 21 '11 at 03:17
2

Use __autoload(). Most major PHP frameworks use autoloading to streamline class loading.

vls
  • 2,304
  • 15
  • 20
  • I did check into __autoload(), but the problem is not including the Class, rather it's creating an instance of an unknown class name of the View type. Using the example above: mypage = new Index(). The framework still needs to know the Index Class. Should I investigate it further. I've read the manual several times? – daganh Apr 20 '11 at 03:02
  • @daganh what do you mean by knowing the Index Class? In the ___autoload() function if it is looking for Index, then you can just include that class. – Flipper Apr 20 '11 at 05:05
  • If I have a file called index.php and within that file I have a class definition with some unknown name that extends a View of my framework. So within index.php i have class support_app{}, but my framework doesn't know that. I tell it specifically by mapping the index.php uri to the support_app class name. However this is done via a database lookup. I want to eleminate this. I can't very well use __autoload() if all I know is the class extends my frameworks View. – daganh Apr 20 '11 at 05:31
  • 2
    No major frameworks use `__autoload`. They use `spl_autoload_register`. – Gordon Apr 20 '11 at 07:01
1

You need some way to map a URL to an object, which is usually handled by a routing component in most frameworks. Might want to take a look at libraries such as https://github.com/chriso/klein.php, or the routing components of the major Frameworks out there (Zend, Symfony, etc.).

rr.
  • 6,484
  • 9
  • 40
  • 48
  • This is more inline with what I'm thinking. I just want to be able upload new pages and have the framework identify them as a View and create an instance of them. I'm currently mapping to a url, but that requires an entry in the database. I'm upvoting for now. – daganh Apr 20 '11 at 03:36
1

Why not to use information from URL to detect correct class? Since you won't use database for static pages, you have to somehow store mapping between URL and class in file. I.e. you will have a file mapping.php:

return array(
  'about' => 'AboutView',
  'copyright' => 'CopyrightView',
);

Here, about and copyright are URL components and values represent appropriate child classes of View. You can have URL's like http://example.com/index.php?page=about and use rewriting capabilities of webserver to make them look good.

You will change implementation of Page::getPageClass() in order to return correct view class. Depending on URL it will use static mappings from the file above or use database.

What's the problem with this approach?

galymzhan
  • 5,505
  • 2
  • 29
  • 45
  • You have me thinking on that one. What if the user browses directly to http://example.com/index.php? I already have support built into the framework to make this work seamlessly. Would it be easier to do something like grep the requested file and extract the Class name between class XXXXX {. Upvoting for fresh alternative. I'm leaning for a regex solution to manually identify the unknown class. I would have thought this problem was common. Maybe I'm over simplifying my startup or maybe its because the framework design is page based. – daganh Apr 20 '11 at 05:49
  • To clarify: Page based design means I physically want an about.php page or copyright.php. I add support for virtual pages utilizing your suggestion for navigation building, but in the end the PageController simply maps them to the actual Page Class. – daganh Apr 20 '11 at 06:01
  • I strongly suggest you to use [front controller](http://en.wikipedia.org/wiki/Front_Controller_pattern), however if you need physically separated files, then you have to put `require_once("Startup.inc");` and `define('requested_page_class', 'About or whatever')` at the top of every file. In the latter case you also don't need file with mappings. – galymzhan Apr 20 '11 at 08:17
  • I streamlined the question into a more specific one and stumbled upon a possible solution. If you have time maybe I can get your opinion or you may possibly want to supply the answer for both questions. Thanks for your help. http://stackoverflow.com/questions/5726934/indentifying-class-names-from-serverscript-filename – daganh Apr 20 '11 at 08:41