Quince PHP Controller


Manual

Apache - The latest version of PHP-Controller depends on Apache. The directory in which the application is deployed must be allowed to use an .htaccess file to manage URLs.

AddType application/x-httpd-php *
php_value include_path ".:Libraries/Smarty-2.6.9/:Libraries/pear/"
DirectoryIndex index.php 
RewriteEngine on
RewriteRule !^(images|js) index.php

PHP - Controller has been tested with PHP 4.4.1 and PHP 5.x.

PEAR - PHP-Controller framework comes bundled with a basic PEAR library. This this library can be replaced and needed. The latest version of Controller only depends on XML_Unserializer and PEAR. The bundled packages are:

Config
Date
DB
Image
PEAR
XML_Parser
XML_RPC
XML_RSS
XML_Serializer
XML_SVG
XML_Tree
XML_Unserializer
XML_Util

Template Engine - PHP-Controller framework bundled with Smarty 2.6.9, but is compatible with version 2.6.9 to 2.6.13. Future versions will attempt to support all versions of Smarty and other PHP template engines. Although the functionality of the controller is template engine agnostic, the autodetection so far only works with Smarty. Future versions will automatically detect other template engines.

If you like to use another template engine, just modify the path in the .htaccess file to your template engine.

Installation

Comming Soon..

Table of Contents

Controller XML Schema

PHP-Controller depends on a specially formated XML file, for which the controller can extract meaning from and properly operate the controller. The rules for this XML are described using XML Schema. The W3C XML Schema Definition Language is an XML language for describing and constraining the content of XML documents (information)

You can view the PHP-Controller schema in http://vault.visudo.com/eddie/PHP-Controller/controller.xsd. If your text editor supports schema validationg, adding the schema information at the top of the XML file, like shown below, automatically validates your XML controller document.:

<?xml version="1.0" encoding="UTF-8"?>
<controller xmlns="http://vault.visudo.com/eddie/PHP-Controller" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://vault.visudo.com/eddie/PHP-Controller controller.xsd">

Editors that validate your XML include: jEdit

The controller.xml is by far the most important file in your application. This is essentially an Access Control List. It limits which classes can be loaded up by the browser. Classes cannot be loaded unless they are defined in controller.xml.

Now lets look at the controller file. The controller gives a path to where the PHP pages are stored. As of version 0.6, relative paths are supported and recommended. Pages are nothing more than PHP classes. We define a name to the page and this name will be how the class is accessed through the browser. We also need to specify the class name. If we are using a template we define it here. We also define which is the first method to be called if this class is loaded with no defined method.

<controller>
  	<domain>http://nailchipper.com/eddie/PHP-Controller/</domain>
  	<pages>/var/www/eddie/PHP-Controller/Pages</pages>
      <page>       <name>admin</name>       <class>Administrator</class>       <template>Administrator</template>       <default-method>users</default-method>    </page> </controller>

To access these classes you can refer to the page name in controller.xml in the URL, like so:

www.example.com/admin

The following are the tags used by controller.xml.

version - *OPTIONAL* The version of the file. This is for the application's internal user

application-path - *OPTIONAL* This tag is required only if the base domain path of your application is not the root of the domain. For example, there is no need to define this tag is the application is running the domain www.example.com, but would need to be defined to "really/cool/app/" if the domain is www.example.com/really/cool/app/

pages - *REQUIRED* This is a relative or absolute path to the directory that holds the classes which controller will manage

authentication *OPTINAL* if your application will have some sections that will be restricted, define the

<authentication>
<name>authentication</name>
<class>Authentication</class>
<template>Authentication</template>
<login-forward method-name="login">administrator</login-forward>
<logout-forward method-name="logout">administrator</logout-forward> 
</authentication>

The following example shows a class that authentication might use. It implements login and logout methods. This can connect to a database, ldap server or hard coded values. The goal is to set the SESSION variable or "role" to the proper group, so user can gain access.

<?php

class Authentication

  function 
login(){
    if(
$_POST["username"] == "eddie" && $_POST["password"] == "mypassword"){
      
$_SESSION['role'] = "admin";
      
$_SESSION['username'] = $_POST["username"];
      
      return 
true;
    }
    else{
      return 
false;
    }
  }
  
  
  function 
logout(){
    
session_unset('role');    
    
session_unset('username');
    
session_destroy();
  }
  
}
?>

page *REQUIRED* (TODO)

Table of Contents

Loading the Controller

In most projects index.php is probably the messiest file. The lack of structure in the PHP programming language (for better or worse) is usually a script file that loads files from many places. Using the PHP-Controller, the flexiability is preserved, but

Example:

<?php

session_start
();
//load database settings
require_once('System/Mysql.class.php');
$_SESSION['database'] = new Mysql("localhost""username""database""password");

//this is all the code we need to load up proper classes
require_once('System/Controller.class.php');
$controller = new Controller("controller.xml");
$controller->setDebugLevel(0);
$controller->performAction();
$templateName $controller->getTemplateName();
$sectionName   $controller->getSectionName();
$methodName   $controller->getMethodName();
$pageContent  $controller->getContent();
$domain =  $controller->getDomainName();

//this should probably be inside a smarty manager class
require_once('System/SmartyManager.class.php');
$smartyManager = new SmartyManager();
$smarty $smartyManager->smartyInitialize('Presentation');

//now we handle the information from controller to template engine. easy?
$smarty->assign("domain"$domain );
$smarty->assign("section"$sectionName );
$smarty->assign("method"$methodName );
$smarty->assign("content"$pageContent);
$smarty->assign("template"$templateName);

$methodTemplate = (strlen($methodName)) ? ".".$methodName null;

$smarty->display($templateName.$methodTemplate.".tpl");

?>

Table of Contents

Controller API

The following methods are used in index.php (EXPAND)

Controller Controller ([string $filename = 'controller.xml'])

string getMethodName ()
mixed  getNavigationState ()

string getSectionName ()
string getTemplateName ()

void   performAction ()

mixed  getContent ()

Table of Contents

Designing Web Classess

Let's begin describing a section in a webpage. In this example we look at a section called "Administrator". Administrator is a PHP class and it will go in the "Pages" directory defined in controller.xml (explained later). Any of the public methods will be accessible through the browser. These methods can get data from GET, POST and, SESSION.

If this were a true MVC application then each method would return the resultset. The controller will be able to get the result by calling the getContent() method and pass it to the view. If you do not want this to be a MVC application, each method can potentially print the HTML directly to screen or return HTML which index.php will simple print.

Simple navigation consists of changing the class name and method name, forms in the other hand process information and quickly redirect the user after form processing is complete. Form processing is defined in the controller.xml and doesn't not require the developer to know what happens after the form is complete.



<?php

class Administrator{

//CONSTRUCTOR, should make DB connections and other initializations    
 function Administrator()

// WEB PUBLIC, methods that are called in browser 
// and package results. 
 function users ()
 function 
modules ()
 function 
search ()


//ACTIONS, perform queries and submit forms and always end in Action
 function addUserAction ()
}

?>

This class can handle most situtions for a website, but if we plan to use our class on situations that the methods no do use GET,POST, or COOKIE, then we have to design a more robust class.

Table of Contents

Accessing Classes

Comming Soon..

Table of Contents

Passing Parameters to Forms

One of the limitations of passing paramters through simple GET or POST is that not all enviroments support GET and POST super globals. So what we need to do is pass this variables to the method through it's parameter's list. The problem is that in most situation the controller would require some knowledge of what paramters to pass. Luckily, PHP-Controller passes these parameters in a "as-you-need" basis. This means that if you define a method with no paramters, then no arguements will be passed, but if define certain variables from Controller, they will be handed to the method automatically.

Here is an example:

<?php
require_once('System/BugsManager.class.php');

class 
Bugs{
  
  function 
viewBug($get){
    
$bugs = new BugsManager();
    return 
$bugs->getBugFromDatbase($get['bug_id']);
  }

  function 
submitBugAction($get$post){
    
$bugs = new BugsManager();
    return 
$bugs->addToDataBase($post['id'], $post['user'], $post['name']); 
  }
}
    
?>

The names of the parameters are not important, but the order of them is. Now if you want to call Parameters::whoAmI(null, array('user'=>1)); and now the class can be used without dependence on GET.

To call a method inside the class just reference it directly in the URL. Arguments to the methods are passed directly through GET and POST using the regular syntax.

www.example.com/Administrator/User?id=1
www.example.com/Administrato r/Users
www.example.com/Administrator/Projects?id=1&user_id=5&other=hello

If no classname is defined in the URL then the controller will look for a tag <default-page> with the value 1.

And load that page. Authentication happens with the <authentication> tag in the form. This tag takes the name, the class in which will handle authentication and the login and logout methods in the class. If any <page> has <role> defined then <authentication> will automatically be called into action and handle permissions.

Table of Contents

Simple Aliases

If you want to shorten the name of URL or simply have a URL that maps to a specific location you can use the <alias> tag in the controller.xml, here you can specify the path with arguments. When the section is loaded it appends all arguments to the arguments already provided.

An alias can be a short cut to a class name like this:

<alias>
<name>admin</name>
<page>Administrator</page>
</alias>

And all the GET variables will be passed to the Administrator class. If the alias is a bit more complicated, like this:

<page>
..
<alias match="admin/projects">Administrator/getProjects?status=enabled</alias>
..
</page>

Then everything passed "admin/projects" in the URL will be considered a GET variable and would be passed down to the Administrator, calling the getProjects method and sending it the GET variable "status=enabled." If our method supports more variables then we can simply pass them by appending them to the end of the alias bit. Like so:

http://www.example.com/admin/projects?project_id=5

The Administrator class will get this:

http://www.example.comAdministrator/getProjects/status=enabled&project_id=5

With this we can hide complexity in the URL and keep the URLs easy to remember.

Table of Contents

Advanced (Multi-variable Matching) Aliases

As of version 0.6, Controller allows user defined multi-variable url parsing and matching. This means that requests may be made using very clean, user-friendly URL’s. Many web programmers will be familiar with the concept of using essentially “fake” directories in URL’s – the popular REST model exemplifies this idea, for example, on the Del.icio.us website.

In Controller, the developer can define such custom URL structures and all url parsing will be handled automatically. For example, imagine you are building an address book module for a large application and that your module’s main interface exists at http://www.example.com/contacts. The /contacts URL would be defined as a page within your controller.xml, and its default method would perhaps be getContactsList(). If you wanted to retrieve a contact from your database by first name and last name, without aliases, you might do something like /contacts/getContact?fname=rasmus&lname=lerdorf. This is fine, but for the end user, the /page/method?argument1=foo&argument2=bar url scheme may not always be the easiest to remember or understand, nor the best looking, if you care about that. With multi-variable url parsing and matching functionality, you can have the URL /contacts/view/lerdorf/rasmus, and your app will extract the values from the URL and understand which value should be assigned to which PHP variable.

From version 0.6 onwards, aliases are usually one-line constructs, associated with <page> elements simply by being contained within them. Thus the contents of the alias tag do not need to contain the name of the page - “contacts” in this case. By contrast, for maximum effectiveness, and in recognition that the naming of pages may not always conveniently coincide with public URL’s, the match tag – to which the URL is actually compared – does need to contain everything after the application path that you wish to see in your aliased URL, but as with all paths in Controller, does not begin with a forward slash.

For Instance:
<alias match="contacts/view/$lastname/$firstname">getContact?firstname=$fname&lname=$lastname</alias>

The match attribute, analogous to the <name> element of aliases in older versions, is an abstracted version of the URL you wish to use, in which all of the parts that you want to be dynamic have been abstracted out - replaced by variables that you name at your discretion. The contents of the <alias> element is the “Internal URL”, starting with the method name, of the URL you wish the alias to access. The values of those parts of the “fake” url that you have replaced with variables can be passed as the values of GET variables in the “real” URL, provided that the variables you use in both places have the same names. Currently there is no support for collisions – i.e. it is not advised to put two aliases that match the same URL as you will likely get unpredictable results.

Table of Contents

Manager Classes (DAOs)

In order to keep your application nicely seperated it is often convinient to create a class which handles all the data access, usually database queries. These classes are generally used to abstract out the complexity (internal plumming) to the designer of the class. For example, someone might right a class called "UserManager" with methods like "addUser" "removeUser" and "editUser". When writing the page a developer uses these methods instead of writing the SQL everytime.

Table of Contents

Building Webservices REST or SOAP

Since pages are designed as classes and do not return any HTML, implementing a webservice is as simple as serializing the output of a page into XML. Controller does not navitely support web services, proper design (which controller strictly requires) allows any page to potentially to be a webservice. The steps required are simple.

First, allow .htaccess to bypass the redirection to index.php. In the example below I allow soap.php and rest.php to be accessed directly. The reason for this is because each one of those files will be in charge of ouputting data, and index.php outputs to smarty.


RewriteRule ^(soap) soap.php [L]
RewriteRule ^(rest) rest.php [L]
RewriteRule !(soap|rest) index.php

This example looks just like index.php,and uses controller.xml for handling requests. Except that the output from each class is turned into an XML file instead of being sent to Smarty.

<?php
session_start
();
//load database settings
require_once('System/Mysql.class.php');
$_SESSION['database'] = new Mysql("localhost""username""database""password");

  
//this is all the code we need to load up proper classes
require_once('System/Controller.class.php');
$controller = new Controller("controller.xml");
$controller->domain .= "rest/"//this is  abit of a hack.
$controller->setDebugLevel(0);
$controller->performAction();
$templateName $controller->getTemplateName();
$sectionName   $controller->getSectionName();
$methodName   $controller->getMethodName();
$pageContent  $controller->getContent();
$domain =  $controller->getDomainName();
$metaData =  $controller->getmetaData();


error_reporting(E_ALL E_NOTICE); 
require_once 
'XML/Serializer.php'
$serializer_options = array ( 
   
'addDecl' => TRUE
   
'encoding' => 'UTF-8'
   
'defaultTagName' => 'item'
   
'indent' => '  '
); 

$Serializer = &new XML_Serializer($serializer_options); 
$status $Serializer->serialize($pageContent); 
if (
PEAR::isError($status)) { 
   die(
$status->getMessage()); 


header('Content-type: text/xml'); 
echo 
$Serializer->getSerializedData(); 
?>

This code below exposes the page class to a web service. A SOAP client will be able to access the page class directly and execute methods in the class as if it were a local object.

<?
require_once('SOAP/Server.php');
require_once(
'SOAP/Disco.php');
require_once(
"Pages/Bugs.class.php");

$webservice = new BugsService();
$server = new SOAP_Server();
$server->addObjectMap($webservice,'http://bugs.example.com/service');


if (isset(
$_SERVER['REQUEST_METHOD'])  &&
  
$_SERVER['REQUEST_METHOD']=='POST') {
  
$server->service($HTTP_RAW_POST_DATA);
} else {
  
// Create the DISCO server
  
$disco = new SOAP_DISCO_Server($server,'Bugs');
  
header("Content-type: text/xml");
  if (isset(
$_SERVER['QUERY_STRING']) &&
     
strcasecmp($_SERVER['QUERY_STRING'],'wsdl') == 0) {
     echo 
$disco->getWSDL();
  } else {
     echo 
$disco->getDISCO();
  }
}
?>