First release of domframework

git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@1207 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
This commit is contained in:
2014-02-27 08:47:54 +00:00
commit 96cf1c4010
26 changed files with 2125 additions and 0 deletions

99
auth.php Normal file
View File

@@ -0,0 +1,99 @@
<?php
class auth
{
/** Display the authentication page
The message is displayed to the user in case of error
The url is the caller url to go back if authentication is correct */
public function pageHTML ($message="", $url="")
{
$res = "";
$res .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"";
$res .= " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
$res .= "<html>\n";
$res .= "<head>\n";
$res .= "<title>Title</title>\n";
$res .= "<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'";
$res .= " />\n";
$res .= "<script src='public/js/jquery.min.js' type='text/javascript'>";
$res .= "</script>\n";
$res .= "<script type='text/javascript' src='public/js/bootstrap.js'>";
$res .= "</script>\n";
$res .= "<link type='text/css' rel='stylesheet' href='";
$res .= "public/css/bootstrap.css'/>\n";
$res .= " <style type='text/css'>\n";
$res .= "body { padding-top: 40px; padding-bottom: 40px;";
$res .= " background-color: #eee; }\n";
$res .= ".form-signin { max-width: 330px;padding:15px;margin:0 auto;}\n";
$res .= ".form-signin .form-signin-heading, .form-signin .checkbox {";
$res .= " margin-bottom: 10px; }\n";
$res .= ".form-signin .checkbox { font-weight: normal; }\n";
$res .= ".form-signin .form-control {";
$res .= "position: relative; font-size: 16px; height: auto;";
$res .= "padding: 10px; -webkit-box-sizing: border-box;";
$res .= "-moz-box-sizing: border-box; box-sizing: border-box; }\n";
$res .= ".form-signin .form-control:focus { z-index: 2; }\n";
$res .= ".form-signin input[type='text'] { margin-bottom: -1px;";
$res .= "border-bottom-left-radius: 0; border-bottom-right-radius: 0; }\n";
$res .= ".form-signin input[type='password'] {";
$res .= "margin-bottom: 10px; border-top-left-radius: 0;";
$res .= "border-top-right-radius: 0; }\n";
$res .= " </style>\n";
$res .= " </head>\n";
$res .= " <body>\n";
$res .= "<div class='container'>\n";
$res .= " <form class='form-signin' role='form' method='post' ";
$res .= "action='#'>\n";
$res .= " <h2 class='form-signin-heading'>"._("Please sign in");
$res .= "</h2>\n";
$res .= " <input type='text' class='form-control' name='email' ";
$res .= "placeholder='"._("Email address")."' required autofocus/>\n";
$res .= " <input type='password' class='form-control' name='password' ";
$res .= "placeholder='"._("Password")."' required/>\n";
$res .= " <label class='checkbox'>";
$res .= "<input type='checkbox' name='remember-me'/>"._("Remember me");
$res .= "</label>\n";
$res .= " <button class='btn btn-lg btn-primary btn-block' ";
$res .= "type='submit'>"._("Sign in")."</button>\n";
if ($message !== "")
$res .= "<div class='alert alert-danger'>$message</div>";
$res .= " </form>\n";
$res .= "</div>\n";
$res .= "</body>\n";
$res .= "</html>\n";
return $res;
}
/** Establish the connection to authentication server */
public function connect ()
{
throw new Exception (_("No connect to authentication available"), 405);
}
/** Check if the email and password are correct
Return TRUE if the authentication is correct
Return an exception if there is a problem */
public function authentication ($email, $password)
{
throw new exception (_("No authentication available"), 405);
}
/** Return all the parameters recorded for the authenticate user */
public function getdetails ()
{
throw new exception (_("No getdetails available"), 405);
}
/** Method to change the password */
public function changepassword ($oldpassword, $newpassword)
{
throw new exception (_("No password change available"), 405);
}
/** List all the users available in the database
Return firstname, lastname, mail, with mail is an array */
public function listusers ()
{
throw new exception (_("No List User available"), 405);
}
}

125
authldap.php Normal file
View File

@@ -0,0 +1,125 @@
<?php
/** User authentication against LDAP server */
class authldap extends auth
{
/** LDAP server : can be ldaps://server.domain.tld if LDAPS */
public $ldapserver="localhost";
public $ldapport=389;
public $ldaptimeout=5;
/** LDAP authentication to search user */
public $ldapauth = "";
public $ldappwd = "";
public $ldapbase = "";
/** Filter used to search user */
public $ldapfilter = "(mail=%s)";
public $ldapfield = "mail";
/** Filter used to find the available datas of an authenticated user */
public $ldapfiltersearch = "(objectClass=inetOrgPerson)";
private $ldapconn = NULL;
private $ldapdnuser = NULL;
function __construct ()
{
if (!function_exists ("ldap_connect"))
throw new Exception ("LDAP support unavailable in PHP", 500);
}
/** Establish a connection to a LDAP server
$server can be "ldaps://ldap.domain:636"
If $user is "", there is no authentication (anonymous mode) */
public function connect ()
{
$this->ldapconn = ldap_connect ($this->ldapserver, $this->ldapport);
if (!$this->ldapconn)
throw new Exception ("Can't contact LDAP server", 500);
ldap_set_option ($this->ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($this->ldapconn, LDAP_OPT_TIMELIMIT, $this->ldaptimeout);
$ldapbind = ldap_bind ($this->ldapconn, $this->ldapauth, $this->ldappwd);
if (ldap_errno ($this->ldapconn) !== 0)
throw new Exception ("Authentication error in pre-auth LDAP", 500);
}
/** Try to authenticate the email/password of the user */
public function authentication ($email, $password)
{
$filter = sprintf ($this->ldapfilter, $email, $email, $email, $email);
$search = ldap_search ($this->ldapconn, $this->ldapbase, $filter,
array ($this->ldapfield));
if ($search === FALSE)
throw new Exception ("Unable to search in LDAP", 500);
$info = ldap_get_entries ($this->ldapconn, $search);
if (!isset ($info["count"]) || $info["count"] !== 1 || !isset ($info[0]) ||
!isset ($info[0]["dn"]))
throw new Exception ("Unable to find the user : '$email'", 401);
$dn = $info[0]["dn"];
$ldapbind2 = @ldap_bind ($this->ldapconn, $dn, $password);
if ($ldapbind2 !== TRUE)
throw new Exception ("Bad password for '$email'", 401);
$this->ldapdnuser = $dn;
}
/** Return all the parameters recorded for the authenticate user */
public function getdetails ()
{
if ($this->ldapdnuser === NULL)
throw new Exception ("No user authenticated !", 401);
$search = ldap_search ($this->ldapconn, $this->ldapdnuser,
$this->ldapfiltersearch);
if ($search === FALSE)
throw new Exception ("Can not found the details for user", 401);
$datas = ldap_get_entries ($this->ldapconn, $search);
$res = array ();
if (isset ($datas[0]))
{
$res = array ("lastname"=>$datas[0]["sn"][0],
"firstname"=>$datas[0]["givenname"][0],
"email"=>$datas[0]["mail"][0]);
}
return $res;
}
/** Method to change the password */
public function changepassword ($oldpassword, $newpassword)
{
throw new Exception (_("The password can't be change for LDAP users"), 405);
}
/** List all the users available in the database
Return firstname, lastname, mail, with mail is an array */
public function listusers ()
{
if ($this->ldapconn === NULL)
throw new Exception ("No established LDAP connection", 500);
$search = ldap_search ($this->ldapconn,$this->ldapbase,
$this->ldapfiltersearch,
array ("mail","sn","givenname"));
if ($search === FALSE)
throw new Exception ("Unable to search the users in LDAP", 500);
$info = ldap_get_entries ($this->ldapconn, $search);
$datas = array ();
foreach ($info as $key=>$vals)
{
if ($key === "count") continue;
if (isset ($vals["sn"][0]) && isset ($vals["givenname"][0]) &&
isset ($vals["mail"]))
{
$datas[$key] = array ("lastname"=>$vals["sn"][0],
"firstname"=>$vals["givenname"][0],
"email"=>$vals["mail"][0]);
}
unset ($datas[$key]["mail"]["count"]);
}
return $datas;
}
function __destruct ()
{
if (isset ($this->ldapconn))
ldap_close ($this->ldapconn);
}
}

64
authorization.php Normal file
View File

@@ -0,0 +1,64 @@
<?php
/** All the needed functions to authorize or deny access to an authenticated
user */
class authorization
{
private $separator = "/";
/** Establish a connexion to the authorization database */
public function connect ()
{
}
/** Return if the user right is NONE, READ, WRITE, EXECUTE
if the object doesn't exists, or is not readable, throw an exception */
public function validate ($object)
{
}
/** Add a new object, with owner and group, and mode bits */
public function add ($object, $owner, $group, $modbits)
{
}
/** Remove the informations about an object */
public function drop ($object)
{
}
/** Change the owner of an object
Need to be the root administrator */
public function chown ($object, $owner)
{
}
/** Change the group of an object
Need to be the owner of the object or the root administrator */
public function chgrp ($object, $group)
{
}
/** Change mode bits for an object
Need to be the owner of the object or the root administrator */
public function chmod ($object, $mod)
{
}
/** Return the mode bits for an object if all his parents are readable for
the user */
public function lsmod ($object)
{
}
/** Return the owner for an object if all his parents are readable for
the user */
public function lsown ($object)
{
}
/** Return the owner for an object if all his parents are readable for
the user */
public function lsgrp ($object)
{
}
}

41
authparams.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
/** Takes the email and the password of the user */
class authparams
{
/** Get informations from $POST variables */
public function post()
{
if (!isset ($_POST["email"]) || !isset ($_POST["password"]))
throw new Exception ("No POST provided", 401);
return array ("email"=>$_POST["email"], "password"=>$_POST["password"]);
}
/** Get informations from previous recorded session */
public function session()
{
if (!isset ($_SESSION))
throw new Exception ("No session previously opened", 401);
if (!isset ($_SESSION["auth"]["email"]) ||
!isset ($_SESSION["auth"]["password"]))
throw new Exception ("No previous email in session", 401);
return array ("email"=>$_SESSION["auth"]["email"],
"password"=>$_SESSION["auth"]["email"]);
}
/** Get informations from a HTTP authentication */
public function http()
{
$realm = _("Restricted access");
if (!isset($_SERVER['PHP_AUTH_USER']))
{
header("WWW-Authenticate: Basic realm=\"$realm\"");
header("HTTP/1.0 401 Unauthorized");
die ($realm);
}
else
{
return array ("email"=>$_SERVER["PHP_AUTH_USER"],
"password"=>$_SERVER["PHP_AUTH_PW"]);
}
}
}

41
authsession.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
/** User authentication against SESSION */
class authsession extends auth
{
function __construct ()
{
if (!isset ($_SESSION))
throw new Exception ("No session previously opened", 401);
}
/** No connection to session */
public function connect ()
{
return TRUE;
}
/** Try to authenticate the email/password of the user */
public function authentication ($email, $password)
{
if (!isset ($_SESSION["auth"]["email"]) ||
!isset ($_SESSION["auth"]["password"]))
throw new Exception ("No previous email in session", 401);
if ($_SESSION["auth"]["email"] !== $email)
throw new Exception ("Unable to authenticate user '$email'", 401);
if ($_SESSION["auth"]["password"] !== $password)
throw new Exception ("Bad password for '$email'", 401);
}
public function getdetails ()
{
return array ("lastname"=>$_SESSION["auth"]["lastname"],
"firstname"=>$_SESSION["auth"]["firstname"],
"email"=>$_SESSION["auth"]["email"]);
}
public function changepassword ($oldpassword, $newpassword)
{
throw new Exception (_("The password can't be change for SESSION users"),
405);
}
}

224
dblayer.php Normal file
View File

@@ -0,0 +1,224 @@
<?php
// dblayer.php
/* Documentation :
The dbLayer provide an abstraction layer on PDO to be easier on all the CRUD
(Create, Read, Update, Delete) operations, accross all the databases engines.
To use it, extends in your code this class, and define the attributes :
- protected $table : name of the table you want to use
- protected $fields : description of all the fields in the database like :
protected $fields = array (
"id"=>array ("integer", "not null", "autoincrement"),
"zone"=>array ("varchar", "255", "not null"),
"viewname"=>array ("varchar", "255"),
"viewclients"=>array ("varchar", "255"),
"comment"=>array ("varchar", "1024"),
"opendate"=>array ("datetime", "not null"),
"closedate"=>array ("datetime"),
);
- protected $primary = "id" ; the primary key of the table (in text). Actually
the dbLayer abstraction don't supports primary key on multiples columns
Optionnaly, you can add the
- protected $debug = TRUE : enable the debug on screen (NOT FOR PROD !!)
*/
/** Permit abstraction on the differents SQL databases available */
class dblayer extends PDO
{
protected $fields = array ();
protected $primary = null;
protected $db = null;
public $debug = FALSE;
/** Return all the tables available in the database */
function listTables ()
{
$driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME);
$rc = @include_once ("dbLayer".ucfirst ($driver).".php");
if ($rc === FALSE)
throw new Exception ("dbLayer driver $driver not available");
}
// TODO !!
/** Create Table creation from $this->fields with engine abstraction
Example in sqlite3 id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
in MySQL id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, */
// TODO !!
/** Create automatic creation of $fields from .schema of sqlite3/
show create table `NomTable`; for MySQL*/
/** Connection to the database engine
See http://fr2.php.net/manual/en/pdo.construct.php for the $dsn format */
function __construct ($dsn, $username=null, $password=null,
$driver_options=null)
{
$this->db = new PDO ($dsn, $username, $password, $driver_options);
$this->db->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/** Create a new entry in the table. Datas must be an indexed array */
function create ($datas)
{
if ($this->db === null)
throw new Exception ("Database not connected");
$datasOK = array ();
// Check for missing parameters
foreach ($this->fields as $key=>$params)
{
if (in_array ("autoincrement", $params))
$datas[$key] = null;
if (in_array ("not null", $params) && !array_key_exists ($key, $datas))
throw new Exception ("Mandatory field '$key' not provided");
// TODO : Check for type inconsistancies before create $datasOK
if (array_key_exists ($key, $datas))
$datasOK[$key] = $datas[$key];
}
$req = "INSERT INTO `".$this->table."` ";
$req .= "(".implode (",", array_keys ($datasOK)).")";
$req .= " VALUES ";
$req .= "(:".implode (",:", array_keys ($datasOK)).")";
if ($this->debug) echo "DEBUG : $req\n";
$st = $this->db->prepare ($req);
foreach ($datasOK as $key=>$val)
{
if ($this->debug) echo "DEBUG BIND : $key->".var_export ($val, TRUE)."\n";
if ($val === null)
$st->bindValue (":$key", $val, PDO::PARAM_NULL);
elseif ($this->fields[$key][0] === "integer")
$st->bindValue (":$key", $val, PDO::PARAM_INT);
elseif ($this->fields[$key][0] === "varchar")
$st->bindValue (":$key", $val, PDO::PARAM_STR);
elseif ($this->fields[$key][0] === "datetime")
$st->bindValue (":$key", $val, PDO::PARAM_STR);
else
throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0]);
}
$st->execute ();
return $this->db->lastInsertId();
}
/** Read all the table or only one line defined by the primary key */
function read ($selectkey=null)
{
if ($this->db === null)
throw new Exception ("Database not connected");
$req = "SELECT ";
$req .= implode (",", array_keys ($this->fields));
$req .= " FROM `".$this->table."`";
if ($selectkey !== NULL)
$req .= " WHERE $this->primary=:primary";
if ($this->debug) echo "DEBUG : $req\n";
$st = $this->db->prepare ($req);
if ($selectkey !== NULL)
{
if ($this->debug) echo "DEBUG BIND : primary->".
var_export ($selectkey, TRUE)."\n";
$st->bindValue (":primary", $selectkey);
}
$st->execute ();
$res = array ();
while ($d = $st->fetch (PDO::FETCH_ASSOC))
$res[] = $d;
return $res;
}
/** Update the key tuple with the provided datas */
function update ($updatekey, $datas)
{
if ($this->db === null)
throw new Exception ("Database not connected");
$datasOK = array ();
// Check for missing parameters
foreach ($this->fields as $key=>$params)
{
// TODO : Check for type inconsistancies before create $datasOK
if (array_key_exists ($key, $datas))
$datasOK[$key] = $datas[$key];
}
$req = "UPDATE `".$this->table."` SET ";
$i = 0;
foreach ($datasOK as $key=>$val)
{
if ($i>0) $req .= ",";
$req .= "$key=:$key";
$i++;
}
$req .= " WHERE $this->primary=:primary";
if ($this->debug) echo "DEBUG : $req\n";
$st = $this->db->prepare ($req);
if ($this->debug) echo "DEBUG BIND : primary->".
var_export ($updatekey, TRUE)."\n";
$st->bindValue (":primary", $updatekey);
foreach ($datasOK as $key=>$val)
{
if ($this->debug) echo "DEBUG BIND : $key->".var_export ($val, TRUE)."\n";
if ($val === null)
$st->bindValue (":$key", $val, PDO::PARAM_NULL);
elseif ($this->fields[$key][0] === "integer")
$st->bindValue (":$key", $val, PDO::PARAM_INT);
elseif ($this->fields[$key][0] === "varchar")
$st->bindValue (":$key", $val, PDO::PARAM_STR);
elseif ($this->fields[$key][0] === "datetime")
$st->bindValue (":$key", $val, PDO::PARAM_STR);
else
throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0]);
}
$st->execute ();
}
/** Delete a tuple identified by its primary key */
function delete ($deletekey)
{
if ($this->db === null)
throw new Exception ("Database not connected");
$req = "DELETE FROM `".$this->table."` ";
$req .= "WHERE $this->primary = :primary";
$st = $this->db->prepare ($req);
if ($this->debug) echo "DEBUG : $req\n";
if ($this->debug) echo "DEBUG BIND : primary->".
var_export ($deletekey, TRUE)."\n";
$st->bindValue (":primary", $deletekey);
$st->execute ();
}
}
/** POC :
error_reporting (E_ALL);
require_once ("domframework/dbLayer.php");
class zone extends dbLayer
{
// The database must be initialized with
// CREATE TABLE `dns_zones` (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
// zone VARCHAR(255) NOT NULL,
// viewname VARCHAR(255),
// viewclients VARCHAR(255),
// comment VARCHAR(1024),
// opendate DATETIME NOT NULL,
// closedate DATETIME,
// UNIQUE (zone,viewname));
protected $table = "dns_zones";
protected $fields = array (
"id"=>array ("integer", "not null", "autoincrement"),
"zone"=>array ("varchar", "255", "not null"),
"viewname"=>array ("varchar", "255"),
"viewclients"=>array ("varchar", "255"),
"comment"=>array ("varchar", "1024"),
"opendate"=>array ("datetime", "not null"),
"closedate"=>array ("datetime"),
);
protected $primary = "id";
}
ini_set ("date.timezone", "Europe/Paris");
$zone = new zone ("sqlite:datas/database.db");
$last = $zone->create (array ("zone"=>"testZone", "opendate"=>date("Y-m-d H:i:s")));
//print_r ($zone->read ());
$zone->update (2040, array ("zone"=>"testZone2"));
print_r ($zone->delete ($last));

15
docs/TODO Normal file
View File

@@ -0,0 +1,15 @@
TODO
----
- Check if the access to model is well closed
- Create the html output with views support and layout
- Do authorization model and restict access to the pages
- Automatic routing to do (to be confirmed by a attribute (off by default)) :
call controller by default, with class/method/param1/param2...
- Automatic typing of document .txt=>outputtxt, .xml=>outputxml... off by
default
- Routing : check if "URL === ROUTE (No variables)" part is needed (can be
catched by regex too)
- Routing : add REST functionnality (to pass the type in the header and provide
the result with HTTP error codes)

185
docs/USAGE Normal file
View File

@@ -0,0 +1,185 @@
DOMFRAMEWORK DOCUMENTATION
==========================
1. Introduction
---------------
The DomFramework is a PHP framework used in CLI or Web sites.
It actually supports some mandatory functionnalities of a framework : routing,
url beautifier, authentication, outputs multiple formats (like html, csvn, json,
xml...).
2. Starting a new project
-------------------------
To start a new project with this framework, put the domframework dir in a folder
defined in "include_path" constant of PHP.ini. Usually, there is a . in it, so
you can add the framework directely in the base of a project if you want, or in
a global system directory.
You can start a new project. The following text will be project in
/var/www/project.
You must add a .htaccess file with :
Options -Indexes
<IfModule mod_rewrite.c>
RewriteEngine on
# if your app is in a subfolder
# RewriteBase /my_app/
# test string is a valid files
RewriteCond %{SCRIPT_FILENAME} !-f
# test string is a valid directory
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)$ index.php?uri=/$1 [NC,L,QSA]
# with QSA flag (query string append),
# forces the rewrite engine to append a query string part of the
# substitution string to the existing string, instead of replacing it.
</IfModule>
# Allow to see a /.html file without having a Forbidden due to Apache conf
<FilesMatch "^\.html">
Satisfy Any
Allow from all
</FilesMatch>
Then start a index.php in the same directory. That's it, the project is started.
3. Creating the folder structure
--------------------------------
DomFramework waits for at least one special folder : controllers.
In this folder, you will create the controllers for your application, with the
name "controller_<CLASS>.php". This file will contain the class <CLASS>.
DomFramework use a folder named "views". It will contains the differents HTML
views rendered on the user browser.
You can create a folder "models" to respect the MVC model. DomFramework forbid
access to this directory directely.
4. Controllers
--------------
A controller is a PHP class which will do something. It shouldn't access to
databases directely (as it is the model part). It can provide methods to access
to database model.
The methods in the controller will be the only access from the framework.
The controllers shouldn't write directly to stdout. They should return the
result of their work to the caller.
Example :
<?php
class blog
{
public function get ($articleid = FALSE)
{
if ($articleid === "5" || $articleid === FALSE)
return array ("5"=>"Article very important");
throw new Exception (_("Article not found"), 404);
}
}
The errors can be returned with an Exception.
5. Routing and beautifier URL
-----------------------------
In a Web site, the philosophy is to have a URL for each action.
You can do a beautiful URL with DomFramework http://localhost/project/blog/page
you will allow the class "blog" to be launch with the parameter "page".
This is the routing. You need to add the routes in your index.php file with :
require_once ("domframework/route.php");
$route = new route ();
$res = $route->routingCollection (array (
"blog/{page}" => array ("class", "method", "{page}"),
));
The routes are read sequentially. The first which match the class/method wins.
It start a new instance of "class" and apply the method "method" with the
parameters need to launch the action. The result of the action is returned to
the main script to be outputed.
The parameters are puts between {}. You must put the parameters in the right
order needed by your method.
There is a special action named "redirect" in the "route" class. It provide the
capability to re-route the Web browser to another page. Put in action :
array ("route", "redirect", "nextPage/", FALSE),
If the URL match the requested search, the Web browser will load immediately a
new page named "nextPage/". The FALSE will be change in case of modular
application (see the MODULAR APPLICATION chapter)
6. Output : the renderer
------------------------
When a controller has finished his job, the result returned to the main class of
routing to be displayed. It can be displayed in a lot of formats : HTML, csv,
json, xml.
The allowed extensions are puts in the routingCollection in the position you
want by adding them between parenthesis. Example :
require_once ("domframework/route.php");
$route = new route ();
$res = $route->routingCollection (array (
"{page}" => array ("class", "method", "{parameter}"),
));
print_r ($res);
It can be used by the renderer system (generally run by controller) :
$renderer = new renderer ();
$renderer->result = $res;
$renderer->output = "html";
$renderer->run ();
In controllers, the renderer can be called too. There is a flash method. The
flash messages will be displayed on next page, in case of HTML page. In CLI, it
is immediately displayed.
7. Layout
---------
In HTML, there is a presentation layer, named layout. The layout is common to
all the site pages.
All the pages are coded in "views" directory. The renderer call them before
displaying the result.
By default, the layout is a page named "layout.html", in the views directory.
It contains the HTML page with some special parts : the tokens. Tokens are
written like {content} or {title}. The renderer replace them by the result of
replacement attibute.
8. Debug
--------
When creating a routingCollection, a lot of errors can be done. There is in
DomFramework the capability to debug the application. You can activate the debug
by putting 1 in the associated method. Example :
$route = new route ();
$route->debug = 1;
The screen will display the actions to help the debug :
==== DEBUG : ROUTING START ====
blog/5 ?? blog/{page} => blog->get({page}) : FOUND : URL === REGEX : Call blog()->get(5)
==== DEBUG : ROUTING END ====
XX. CLI usage
-------------
The DomFramework is designed to be used in console line too.
TBC !!
XX. Modular application
-----------------------
THe DomFramework can be used on modular applications. Each application (or
module) will have a complete autonomy. The structure will be :
/Project/
/Project/module1/
/Project/module2/
[/Project/domframework/ is optional]
/ ... /
Only one instance of DomFramework is needed. It can be put outside the project.
In this mode, the routing must be adapted to use the modular mode :
$route = new route ();
$route->module = TRUE;
If you use the route redirect method, think to change the last parameter to
TRUE.
Each module will have its controllers/models/views folders.
XX. Plugin developpment
-----------------------
DomeFramework is based on plugins. You can develop a new plugin to an other
output type (like a graph...), a new authentication method, etc.
Usually, this is done by extending the main class and adding the specificity of
the new methods.

21
examples/blog/.htaccess Normal file
View File

@@ -0,0 +1,21 @@
Options -Indexes
<IfModule mod_rewrite.c>
RewriteEngine on
# if your app is in a subfolder
# RewriteBase /my_app/
# test string is a valid files
RewriteCond %{SCRIPT_FILENAME} !-f
# test string is a valid directory
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)$ index.php?uri=/$1 [NC,L,QSA]
# with QSA flag (query string append),
# forces the rewrite engine to append a query string part of the
# substitution string to the existing string, instead of replacing it.
</IfModule>
# Allow to see a /.html file without having a Forbidden due to Apache conf
<FilesMatch "^\.html">
Satisfy Any
Allow from all
</FilesMatch>

View File

@@ -0,0 +1,127 @@
<?php
error_reporting (E_ALL);
class blog
{
/** Return an article */
private function get ($articleid = FALSE)
{
require_once ("models/model_file.php");
$data = new model_file();
return $data->get ($articleid);
}
/** Return an article in text */
public function getTxt ($articleid = FALSE)
{
try
{
$res = $this->get ($articleid);
$renderer = new renderer ();
$renderer->output = "txt";
$renderer->result = $res;
$renderer->run ();
exit;
}
catch (Exception $e) {};
}
/** Return an article in xml */
public function getXml ($articleid = FALSE)
{
try
{
$res = $this->get ($articleid);
$renderer = new renderer ();
$renderer->output = "xml";
$renderer->result = $res;
$renderer->run ();
exit;
}
catch (Exception $e) {};
}
/** Return an article in csv */
public function getCsv ($articleid = FALSE)
{
try
{
$res = $this->get ($articleid);
$renderer = new renderer ();
$renderer->output = "csv";
$renderer->result = $res;
$renderer->run ();
exit;
}
catch (Exception $e) {};
}
/** Return an article in json */
public function getJson ($articleid = FALSE)
{
try
{
$res = $this->get ($articleid);
$renderer = new renderer ();
$renderer->output = "json";
$renderer->result = $res;
$renderer->run ();
exit;
}
catch (Exception $e) {};
}
/** Return an article in HTML */
public function getHtml ($articleid = FALSE)
{
try
{
$res = $this->get ($articleid);
$renderer = new renderer ();
$renderer->output = "html";
$renderer->result = $res;
$renderer->viewClass = "view_blog";
$renderer->viewMethod = "get";
$renderer->layout = "layout";
// $renderer->title = "Super Title";
$renderer->run ();
exit;
}
catch (Exception $e)
{
// TODO !!
echo "404 with ".$e->getMessage();
exit;
};
}
/** Return the listing of all the recorded articles */
public function listing ()
{
require_once ("models/model_file.php");
$data = new model_file();
return $data->listing ();
}
/** Return the listing of all the recorded articles in HTML */
public function listingHtml ()
{
try
{
$res = $this->listing ();
$renderer = new renderer ();
$renderer->output = "html";
$renderer->result = $res;
$renderer->viewClass = "view_blog";
$renderer->viewMethod = "listing";
$renderer->layout = "layout";
$renderer->run ();
exit;
}
catch (Exception $e)
{
// TODO !!
echo "404 with ".$e->getMessage();
exit;
};
}
}

View File

@@ -0,0 +1 @@
deny from all

2
examples/blog/datas/5 Normal file
View File

@@ -0,0 +1,2 @@
<h1>Article 5</h1>
<p>Lorem ipsum</p>

16
examples/blog/index.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
require_once ("domframework/route.php");
require_once ("domframework/renderer.php");
$route = new route ();
$route->debug =0 ;
$res = $route->routingCollection (array (
//"" => array ("route", "redirect", "/1", FALSE),
"" => array ("blog", "listingHtml"),
"{page}.xml" => array ("blog", "getXml", "{page}"),
"{page}.html" => array ("blog", "getHtml", "{page}"),
"{page}.csv" => array ("blog", "getCsv", "{page}"),
"{page}.json" => array ("blog", "getJson", "{page}"),
"{page}.txt" => array ("blog", "getTxt", "{page}"),
"{page}" => array ("blog", "getHtml", "{page}"),
));
echo "404 !";

View File

@@ -0,0 +1,36 @@
<?php
/** Model_file : store the blog datas in files */
class model_file
{
public $dataPath = "datas/";
/** Return a provided article */
function get ($articleid = FALSE)
{
$this->dataPath = realpath ($this->dataPath);
if ($this->dataPath === FALSE)
throw new Exception (_("Folder data not available"), 500);
$fileArticle = $this->dataPath."/".$articleid;
if (! file_exists ($fileArticle))
throw new Exception (_("Article not found"), 404);
$fileArticle = realpath ($fileArticle);
if (substr ($fileArticle, 0, strlen ($this->dataPath)) !== $this->dataPath)
throw new Exception (_("Asked article not in data path"), 404);
if ($fileArticle === FALSE)
throw new Exception (_("Article not found"), 404);
return file_get_contents ($fileArticle);
}
/** Return a list of all the articles ID */
function listing ()
{
$list = glob ($this->dataPath."/*");
if ($list === FALSE || empty ($list))
throw new Exception (_("No article found"), 404);
foreach ($list as $key=>$val)
$list[$key] = basename ($val);
return $list;
}
}

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{title}</title>
</head>
<body>
{content}
</body>
</html>

View File

@@ -0,0 +1,21 @@
<?php
class view_blog
{
function get ($data)
{
return $data;
}
function listing ($data)
{
$content = "<ul>\n";
foreach ($data as $val)
{
$content .= " <li>";
$content .= "<a href='".urlencode ($val)."'>".htmlentities ($val)."</a>";
$content .= "</li>\n";
}
$content .= "</ul>\n";
return $content;
}
}

406
form.php Normal file
View File

@@ -0,0 +1,406 @@
<?php
error_reporting (E_ALL);
class form
{
/** This class permit to create easily some forms to HTML (or text mode in
future).
Each field can be checked in AJAX or HTML. */
private $fields = NULL;
private $formName;
public $debug=0;
function __construct ($formName = "form")
{
$this->formName = $formName;
}
public function fields ($fields)
{
/** Save the fields into the structure.
Available :
- name : name of the field in the HTML page
- label : label written to the describe the field
- [titles] : text written in radio/checkboxes
- [defaults] : default values. Must be array for checkbox/select, and
string for others
- [type] : text, password, hidden, checkbox, select, radio, submit
text by default
- [multiple] : Multiple selection are possible (if the type supports it)
- [group] : define a fieldset and define the title with groupe name
Warning : all the elements of the same group must be
consecutive !
- [readonly] : put a read-only flag on the field (the user see it but
can't interract on it. The value will be sent to next
page
- [verify] : Tests to verify with error priority and associated
message (%s is replaced by field selected value). Order
test from main tests to minor tests.
\$tmpfield can be used as a copy of the current field,
to check the defaults per example
- [error] : array containing (error|warning) => message
*/
$this->fields = $fields;
}
public function verify (&$fieldsVerify, $valuesVerify = NULL)
{
/** Return TRUE if the value associated to a field is correct. Return an
array with a severity and a message to explain why a field is not
correct.
Fields can be an array with just one element, then only this element is
checked */
$ret = array ();
if ($this->debug)
echo "<pre>";
foreach ($fieldsVerify as $field)
{
if (!isset ($field->verify))
continue;
if (!isset ($valuesVerify[$field->name]))
throw new Exception ("No value provided for $field->name", 500);
foreach ($field->verify as $test => $message)
{
$func = sprintf ($test, addslashes ($valuesVerify[$field->name]));
if ($this->debug)
echo "VERIFY: \"$func\" => ";
$tmpfield = $field;
$res = addslashes (serialize ($tmpfield));
// TODO : http://fr2.php.net/manual/en/reflectionfunction.invokeargs.php
// to remove eval ?
$rc = eval ("\$tmpfield=unserialize(stripslashes('$res'));".
"return $func;");
if ($this->debug)
var_dump ($rc);
if ($rc !== FALSE)
{
$ret[$field->name] = $message;
$field->error = $message;
break;
}
}
}
if ($this->debug)
{
echo "RESULT: ";
var_dump ($ret);
echo "</pre>";
}
return $ret;
}
public function printHTML ($method = 'post', $values = NULL)
{
/** Return the fields in HTML code. If $values is provided, use it in place
of default values. In case of select boxes, $values are the selected
elements
$method is the method written in method field of <form> */
// TODO : textarea, file
$res = "";
$res = "<form action='#' method='$method'";
if ($this->formName != "")
$res .= " id='$this->formName'";
$res .= ">\n";
$group = "";
foreach ($this->fields as $field)
{
if (isset ($field->group) && $field->group !== $group)
{
$res .= "<fieldset>\n";
$res .= " <legend>$field->group</legend>\n";
$group = $field->group;
}
$res .=" ";
switch ($field->type)
{
case "checkbox":
// No $field->multiple
// values !
$res .= "<div class='form-group";
if (isset ($field->error))
$res .= " has-".key ($field->error);
$res .= "'>\n";
$res .= " <label class='control-label' for='".$this->formName."_".
htmlspecialchars ($field->name, ENT_QUOTES)."_0'>";
$res .= htmlspecialchars ($field->label)."</label>\n";
$res .= " <div class='controls'>\n";
if (is_string ($field->defaults))
$field->defaults = array ($field->defaults);
foreach ($field->titles as $key=>$val)
{
$res .= " <input type='hidden'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."][$key]'";
$res .= " value='unset'";
$res .= "/>";
$res .= "<input type='checkbox'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."][$key]'";
$res .= " id='$this->formName"."_".
htmlspecialchars ($field->name, ENT_QUOTES)."_$key'";
if (isset ($field->readonly) && $field->readonly !== FALSE)
$res .= " disabled='disabled'";
if (isset ($values[$field->name][$key]) &&
$values[$field->name][$key] !== "unset")
$res .= " checked='checked'";
elseif (isset ($field->defaults[$key]) &&
$field->defaults[$key] !== "")
$res .= " checked='checked'";
$res .= " class='form-control'";
$res .= "/>";
$res .= "$val\n";
}
if (isset ($field->error))
$res .= " <span class='help-block'>".reset ($field->error).
"</span>\n";
$res .= " </div>\n"; // End controls
$res .= " </div>\n"; // End form-group
break;
case "hidden":
// No $field->label, $field->multiple, $field->readonly
$res .= "<input type='hidden'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."]'";
$res .= " id='$this->formName"."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'";
if (isset ($values[$field->name]))
$res .= " value='".htmlspecialchars ($values[$field->name])."'";
else
$res .= " value='".htmlspecialchars ($field->defaults)."'";
$res .= "/>\n";
break;
case "password":
// No $field->multiple
$res .= "<div class='form-group";
if (isset ($field->error))
$res .= " has-".key ($field->error);
$res .= "'>\n";
$res .= " <label class='control-label' for='".$this->formName."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'>";
$res .= htmlspecialchars ($field->label)."</label>\n";
$res .= " <div class='controls'>\n";
$res .= " <input type='password'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."]'";
$res .= " id='$this->formName"."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'";
if (isset ($values[$field->name]))
$res .= " value='".htmlspecialchars ($values[$field->name],
ENT_QUOTES)."'";
else
$res .= " value='".htmlspecialchars ($field->defaults, ENT_QUOTES).
"'";
if (isset ($field->readonly) && $field->readonly !== FALSE)
$res .= " readonly='readonly'";
$res .= " class='form-control'";
$res .= "/>\n";
if (isset ($field->error))
$res .= " <span class='help-block'>".reset ($field->error).
"</span>\n";
$res .= " </div>\n"; // End controls
$res .= " </div>\n"; // End form-group
break;
case "radio":
// No $field->multiple
$res .= "<div class='form-group";
if (isset ($field->error))
$res .= " has-".key ($field->error);
$res .= "'>\n";
$res .= " <label class='control-label' for='".$this->formName."_".
htmlspecialchars ($field->name, ENT_QUOTES)."_0'>";
$res .= htmlspecialchars ($field->label)."</label>\n";
$res .= " <div class='controls'>\n";
if (is_string ($field->defaults))
$field->defaults = array ($field->defaults);
$res .= " <input type='hidden'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."]'";
$res .= " value='unset'";
$res .= "/>\n";
foreach ($field->titles as $key=>$val)
{
$res .= " <label class='radio'>";
$res .= "<input type='radio'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."]'";
$res .= " id='$this->formName"."_".
htmlspecialchars ($field->name, ENT_QUOTES)."_$key'";
$res .= " value='".htmlspecialchars ($val, ENT_QUOTES)."'";
if (isset ($field->readonly) && $field->readonly !== FALSE)
$res .= " readonly='readonly'";
if (isset ($values[$field->name]) &&
$values[$field->name] === $val)
$res .= " checked='checked'";
elseif (isset ($field->defaults[0]) &&
$field->defaults[0] === $val)
$res .= " checked='checked'";
$res .= " class='form-control'";
$res .= "/>";
$res .= "$val";
$res .= "</label>\n"; // End label radio
}
if (isset ($field->error))
$res .= " <span class='help-block'>".reset ($field->error).
"</span>\n";
$res .= " </div>\n"; // End controls
$res .= " </div>\n"; // End form-group
break;
case "select":
// $values->$field
$res .= "<div class='form-group";
if (isset ($field->error))
$res .= " has-".key ($field->error);
$res .= "'>\n";
$res .= " <label class='control-label' for='".$this->formName."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'>";
$res .= htmlspecialchars ($field->label)."</label>\n";
$res .= " <div class='controls'>\n";
if (isset ($field->defaults) && is_array ($field->defaults))
{
if (isset ($field->readonly) && $field->readonly !== FALSE)
{
foreach ($field->defaults as $key=>$val)
{
$res .= "<input type='hidden'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."][".
htmlspecialchars ($key, ENT_QUOTES)."]'";
$res .= " value='";
$res .= htmlspecialchars ($val, ENT_QUOTES)."'";
$res .= "/>";
}
}
$res .= " <select";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."]";
if (isset ($field->multiple) && $field->multiple !== FALSE)
$res .= "[]";
$res .= "'";
$res .= " id='$this->formName"."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'";
if (isset ($field->multiple) && $field->multiple !== FALSE)
$res .= " multiple='multiple'";
if (isset ($field->readonly) && $field->readonly !== FALSE)
$res .= " disabled='disabled'";
$res .= " class='form-control'";
$res .= ">\n";
foreach ($field->defaults as $key=>$val)
{
$res .= " <option value='";
$res .= htmlspecialchars ($key, ENT_QUOTES)."'";
if (isset ($values[$field->name]) &&
is_string ($values[$field->name]) &&
$values[$field->name] == $key)
$res .= " selected='selected'";
elseif (isset ($values[$field->name]) &&
is_array ($values[$field->name]) &&
in_array ($key, $values[$field->name]))
$res .= " selected='selected'";
$res .= ">";
$res .= htmlspecialchars ($val);
$res .= "</option>\n";
}
$res .= " </select>\n";
if (isset ($field->error))
$res .= " <span class='help-block'>".reset ($field->error).
"</span>\n";
}
else
{
$res .= _("No value provided");
}
$res .= " </div>\n"; // End controls
$res .= " </div>\n"; // End form-group
break;
case "submit":
// No $field->label, $field->multiple, $field->error
$res .= "<input type='submit'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."]'";
$res .= " id='$this->formName"."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'";
if (isset ($field->readonly) && $field->readonly !== FALSE)
$res .= " disabled='disabled'";
if (isset ($field->defaults))
$res .= " value='".htmlspecialchars ($field->defaults, ENT_QUOTES).
"'";
$res .= " class='form-control'";
$res .= "/>\n";
break;
default:
// No $field->multiple, $field->titles
$res .= "<div class='form-group";
if (isset ($field->error))
$res .= " has-".key ($field->error);
$res .= "'>\n";
$res .= " <label class='control-label' for='".$this->formName."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'>";
$res .= htmlspecialchars ($field->label)."</label>\n";
//$res .= " <div class='controls'>\n";
$res .= " <input type='text'";
$res .= " name='$this->formName"."[".
htmlspecialchars ($field->name, ENT_QUOTES)."]'";
$res .= " id='$this->formName"."_".
htmlspecialchars ($field->name, ENT_QUOTES)."'";
if (isset ($values[$field->name]))
$res .= " value='".htmlspecialchars ($values[$field->name],
ENT_QUOTES)."'";
else
$res .= " value='".htmlspecialchars ($field->defaults, ENT_QUOTES).
"'";
if (isset ($field->readonly) && $field->readonly !== FALSE)
$res .= " readonly='readonly'";
$res .= " class='form-control'";
$res .= "/>\n";
if (isset ($field->error))
$res .= " <span class='help-block'>".reset ($field->error).
"</span>\n";
//$res .= " </div>\n"; // End controls
$res .= " </div>\n"; // End form-group
break;
}
if (isset ($field->group) && $field->group !== $group ||
!isset ($field->group) && $group !== "")
{
$res .="</fieldset>\n";
$group = "";
}
}
$res .= "</form>\n";
return $res;
}
}
class formfield
{
public $name;
public $label;
public $titles;
public $defaults;
public $type;
public $multiple;
public $group;
public $readonly;
public $verify;
public $error;
function __construct ($name, $label)
{
$this->name = $name;
$this->label = $label;
}
}

55
http.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
class http
{
/** Choose the best choice from user choices.
Can be used for languages (HTTP_ACCEPT_LANGUAGE), type of pages
(HTTP_ACCEPT)...
Ex. fr, en-gb;q=0.8, en;q=0.7
Ex. text/html,application/xhtml+xml,application/xml;q=0.9,* /*;q=0.8
If available is empty, then return the best priority defined by user,
and throw an exception if nothing is provided for by the user.
If nothing match, return $default
*/
function bestChoice ($uservar, $available=array(), $default=FALSE)
{
$uservar = str_replace (" ", "", $uservar);
$userchoices = explode (",", $uservar);
if (!isset ($userchoices[0]))
{
if ($default === FALSE)
throw new Exception ("No user choice provided to bestChoice", 500);
return $default;
}
// Remove weights (provided by the order of the user)
// TODO : Reorganise the order if the weights are not in the right order
foreach ($userchoices as $key=>$val)
{
$vals = @explode (";q", $val);
$userchoices[$key] = $vals[0];
}
if (count ($available) === 0)
return $userchoices[0];
// Look at the best existing solution
foreach ($userchoices as $choice)
{
foreach ($available as $avail)
{
if (strtolower ($avail) === strtolower ($choice))
return $avail;
// Case en_US
$availTmp = str_replace ("_", "-", $avail);
if (strtolower ($availTmp) === strtolower ($choice))
return $avail;
// Case text/xml, application/csv (just compare xml or csv)
$mimes = explode ("/", $choice);
if (isset ($mimes[1]) && strtolower ($availTmp) === $mimes[1])
return $avail;
}
}
// No best solution found. Use the default available solution
return $default;
}
}

9
output.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
/** Class used to display datas */
class output
{
public function out ($data)
{
throw new Exception ("No type of output selected");
}
}

22
outputcsv.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
require_once ("output.php");
class outputcsv extends output
{
public function out ($data)
{
if (!function_exists ("fputcsv"))
throw new Exception ("CSV support not available in PHP !", 500);
if (!is_array ($data))
$data = array ($data);
@header("Cache-Control: no-store, no-cache, must-revalidate");
@header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
@header('Last-Modified: '.gmdate ('D, d M Y H:i:s').' GMT');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
@header ("Content-Type: text/csv");
// TODO : return $csv
$out = fopen ('php://output', 'w');
fputcsv ($out, $data);
fclose ($out);
}
}

42
outputhtml.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
require_once ("output.php");
class outputhtml extends output
{
/** Data is printed by viewClass->viewmethod, in the middle of $layout
title is put in the title of the HTML page
$replacement modify the result (it can do title too :
array ("{title}"=>"title to display") */
public function out ($data, $title = FALSE,
$viewClass = FALSE, $viewMethod = FALSE,
$layout = FALSE, $replacement = array())
{
@header("Cache-Control: no-store, no-cache, must-revalidate");
@header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
@header('Last-Modified: '.gmdate ('D, d M Y H:i:s').' GMT');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
@header ("Content-Type: text/html");
$resView = $data;
if ($viewClass !== FALSE && $viewMethod !== FALSE)
{
require ("views/$viewClass.php");
$reflection = new ReflectionMethod ($viewClass, $viewMethod);
$resView = $reflection->invokeArgs (new $viewClass, array ($data));
}
if ($layout !== FALSE)
{
$layoutPage = file_get_contents ("views/$layout.html");
$resView = str_replace ("{content}", $resView, $layoutPage);
}
// Do the title replacement in the replacement structure
if (! isset ($replacement["{title}"]))
$replacement["{title}"] = $title;
foreach ($replacement as $key=>$val)
$resView = str_replace ($key, $val, $resView);
return $resView;
}
}

18
outputjson.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
require_once ("output.php");
class outputjson extends output
{
public function out ($data)
{
if (!function_exists ("json_encode"))
throw new Exception ("JSON support not available in PHP ! apt-get ".
"install php5-json", 500);
@header("Cache-Control: no-store, no-cache, must-revalidate");
@header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
@header('Last-Modified: '.gmdate ('D, d M Y H:i:s').' GMT');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
@header ("Content-Type: application/json");
echo json_encode ($data);
}
}

28
outputtxt.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
require_once ("output.php");
class outputtxt extends output
{
public function out ($data)
{
@header("Cache-Control: no-store, no-cache, must-revalidate");
@header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
@header('Last-Modified: '.gmdate ('D, d M Y H:i:s').' GMT');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
@header ("Content-Type: text/plain");
if ($data === TRUE)
echo "OK\n";
elseif ($data === FALSE)
echo "FALSE\n";
elseif (is_string ($data))
echo "$data\n";
elseif (is_integer ($data))
echo "$data\n";
elseif (is_array ($data))
echo substr (print_r ($data, TRUE), 8, -3)."\n";
elseif ($data === NULL)
echo "NULL\n";
else
echo "TODO : ".gettype ($rc)." on ".__FILE__.":".__LINE__."\n";
}
}

40
outputxml.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
require_once ("output.php");
class outputxml extends output
{
public function out ($data)
{
@header("Cache-Control: no-store, no-cache, must-revalidate");
@header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
@header('Last-Modified: '.gmdate ('D, d M Y H:i:s').' GMT');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
@header ("Content-Type: text/xml");
$xml = new SimpleXMLElement ("<?xml version=\"1.0\"?><root></root>");
// function call to convert array to xml
if (!is_array ($data))
$data = array ($data);
$this->array_to_xml ($data, $xml);
//saving generated xml file
print $xml->asXML();
}
// function defination to convert array to xml
private function array_to_xml ($data, &$xml)
{
foreach($data as $key => $value)
{
if(is_array($value))
{
$key = is_numeric($key) ? "item$key" : $key;
$subnode = $xml->addChild("$key");
array_to_xml($value, $subnode);
}
else
{
$key = is_numeric($key) ? "item$key" : $key;
$xml->addChild("$key","$value");
}
}
}
}

86
renderer.php Normal file
View File

@@ -0,0 +1,86 @@
<?php
class renderer
{
public $result = NULL;
public $output = NULL;
public $title = " "; // Title by default : space to be compatible with HTML5
/** Filename of class containing the presentation layer */
public $viewClass = FALSE;
/** Method apply to class object to display the $result */
public $viewMethod = FALSE;
/** Filename in views containing the HTML layout. Without .html at the end */
public $layout = FALSE;
/** Display the $this->result result in the output model defined by
$this->output */
public function run ()
{
if (!@require_once ("output$this->output.php"))
throw new Exception ("Renderer '$this->output' not found", 500);
$class = "output$this->output";
$reflection = new ReflectionMethod ($class, "out");
$res = $reflection->invokeArgs (new $class,
array ($this->result, $this->title,
$this->viewClass, $this->viewMethod, $this->layout));
echo $res;
}
/** Add a flash message to the user, on the next HTML page.
In CLI, display the message immediately.
Message must be translated before sending to the function
Priority is 1:success, 2:info, 3:warning, 4:error
or textual : SUCCESS, INFO, WARNING, ERROR */
static public function flash ($priority, $message)
{
if (php_sapi_name () === "cli")
{
switch ($priority)
{
case 1:
case "SUCCESS":
echo "SUCCESS: $message\n";
break;
case 2:
case "INFO":
echo "INFO: $message\n";
break;
case 3:
case "WARNING":
echo "WARNING: $message\n";
break;
case 4:
case "ERROR":
echo "ERROR: $message\n";
break;
default:
throw new Exception ("Unknown flash priority '$priority'", 500);
}
}
else
{
if (! isset ($_SESSION))
throw new Exception ("No session available to store flash", 500);
switch ($priority)
{
case 1:
case "SUCCESS":
$_SESSION["renderer"]["flash"][] = array (1, $message);
break;
case 2:
case "INFO":
$_SESSION["renderer"]["flash"][] = array (2, $message);
break;
case 3:
case "WARNING":
$_SESSION["renderer"]["flash"][] = array (3, $message);
break;
case 4:
case "ERROR":
$_SESSION["renderer"]["flash"][] = array (4, $message);
break;
default:
throw new Exception ("Unknown flash priority '$priority'", 500);
}
}
}
}

391
route.php Normal file
View File

@@ -0,0 +1,391 @@
<?php
error_reporting (E_ALL);
class route
{
private $baseURL = "";
private $baseURLmodule = "";
public $module = NULL;
public $debug=0; // 0:NoDebug, 1:routing, 2:more debug (developpement)
//public $defaultOutput = "html"; // Default renderer : html
function __construct ()
{
}
/** Return the baseURL of the site
Always finish with a slash */
function baseURL ($module = FALSE)
{
if ($this->module === NULL)
$this->module = $module;
if ($this->baseURL !== "")
return $this->baseURL;
if (isset ($_SERVER["SCRIPT_NAME"]))
$this->baseURL = dirname ($_SERVER["SCRIPT_NAME"]);
if (isset ($_SERVER["SERVER_NAME"]))
$this->baseURL = "//".$_SERVER["SERVER_NAME"].$this->baseURL;
if (isset ($_SERVER["HTTPS"]))
$this->baseURL = "https:".$this->baseURL;
else
$this->baseURL = "http:".$this->baseURL;
if (substr ($this->baseURL, -1) !== "/")
$this->baseURL .= "/";
$this->baseURLmodule = $this->baseURL;
// Only != NOT !== (cause : $this->module can be converted in string "0")
if ($this->module != FALSE)
{
$this->baseURLmodule = $this->baseURL;
$this->baseURL = dirname ($this->baseURL)."/";
}
return $this->baseURL;
}
/** Return the baseURL of the module
Always finish with a slash */
function baseURLmodule ()
{
if ($this->baseURLmodule !== "")
return $this->baseURLmodule;
$this->baseURL ($this->module);
return $this->baseURLmodule;
}
function baseURLset ($baseURL)
{
$this->baseURL = $baseURL;
}
/** Return the complete URL used to see this page */
function requestURL ()
{
if (isset ($_SERVER["HTTPS"]))
$url = "https:";
else
$url = "http:";
if (isset ($_SERVER["SERVER_NAME"]))
$url .= "//".$_SERVER["SERVER_NAME"];
if (isset ($_SERVER["REQUEST_URI"]))
$url .= $_SERVER["REQUEST_URI"];
return $url;
}
/** Do all the routing with redirections
$destURL can be absolute or relative
If module is set, the site is modular and a directory is named with module
name */
function redirect ($destURL, $module)
{
if (php_sapi_name () === "cli")
exit;
$destURL = trim ($destURL);
$baseURL = $this->baseURL ($module);
$requestURL = $this->requestURL ();
if (substr ($requestURL, -1) !== "/")
$requestURL .= "/";
if ($destURL[0] === "/")
{
// Absolute : return to project base
$destURL = $baseURL.substr ($destURL, 1);
}
if (substr ($destURL, 0, 4) !== "http")
{
// Relative to parameters : continue
$destURL = $requestURL.$destURL;
}
// Else http : keep the complete URL
if ($destURL === "")
throw new Exception ("Destination URL is empty", 500);
if ($destURL === $requestURL)
throw new Exception ("Redirect to myself", 508);
if (substr ($destURL, 0, strlen ($baseURL)) !== $baseURL)
throw new Exception ("Can't redirect outside this site (Base $baseURL)",
405);
if ($this->debug)
{
echo "<pre>\n";
echo "==== DEBUG : REDIRECT START ====\n";
echo "BASEURL=$baseURL\n";
echo "REQUURL=$requestURL\n";
echo "destURL=$destURL\n";
echo " ---> Redirect to <a href='$destURL'>$destURL</a>\n";
echo "==== DEBUG : REDIRECT END ====\n";
echo "</pre>\n";
exit;
}
if (! headers_sent ())
{
header ("Cache-Control: no-store, no-cache, must-revalidate");
header ("Pragma: no-cache");
header ("Location: $destURL");
}
else
{
echo "<script type='text/javascript'>\n";
echo "window.location.replace('$destURL');</script>";
echo "<a href='$destURL'>Redirect to $destURL</a>";
}
exit;
}
/** Do all the routing actions
wait an array with search=>action
- search can be the end of URL and optionnal parameters in {var1} format
{var1} must be the name of the parameter of the method
- action is an array with (0=>CLASS, 1=>method, 2...=>parameters) */
function routingCollection ($routes)
{
if ($this->debug)
echo "<pre>==== DEBUG : ROUTING START ====\n";
foreach ($routes as $route=>$action)
{
$url = substr ($this->requestURL (), strlen ($this->baseURLmodule ()));
if ($this->debug)
{
echo "==> $url ?? $route => ".$action[0]."->".$action[1]."(";
echo implode (", ", array_slice ($action, 2)).") : ";
// Flush to debug redirect if javascript is disabled
ob_flush ();
flush ();
}
$class = $action[0];
$method = $action[1];
$params = array_slice ($action, 2);
/* This part is actually disabled to be simpler
// Asked output
$outputAuthorized = array ("html");
// -- Look at authorized outputs
$regex = "#\((.+)\)#U";
unset ($matches);
$rcRegex = preg_match ($regex, $route, $matchesExt);
if ($rcRegex !== FALSE && $rcRegex !== 0)
{
// Defined output provided
$outputAuthorized = explode (".", $matchesExt[1]);
$routeOutput = $matchesExt[0];
$regexOutput = "(?P<extension>";
foreach ($outputAuthorized as $k=>$m)
{
if ($m === "")
{
$regexOutput .= "";
}
else
{
$outputAuthorized[$k] = ".$m";
$regexOutput .= "|\\.$m";
}
}
$regexOutput .= ")";
}
if ($this->debug)
echo "\n";
if ($this->debug > 1)
{
echo " -- AUTHORIZED OUTPUT\n";
echo "routeOutput = $routeOutput\n";
echo "regexOutput = ".htmlentities ($regexOutput)."\n";
echo "outputAuthorized = ";print_r ($outputAuthorized);
echo " -- LOOK AT EXTENSION IN URL : SAVE IT AND REMOVE IF FOUND\n";
}
$regex = "#^$route$#U";
$regex = str_replace ("{", "(?P<", $regex);
$regex = str_replace ("}", ">.+)", $regex);
$regex = str_replace ($routeOutput, $regexOutput, $regex);
if ($this->debug > 1)
echo "regex = ".htmlentities ($regex)."\n";
unset ($matches);
$rcRegex = preg_match ($regex, $url, $matches);
if ($rcRegex !== FALSE && $rcRegex !== 0)
{
if ($this->debug > 1)
{
echo "MATCHES=";print_r ($matches);
}
if (isset ($matches["extension"]) && $matches["extension"] !== "")
{
// Authorized extension found : save it and remove it from URL
$this->output = substr ($matches["extension"], 1);
if ($this->debug)
echo "Extension found in url : output='".$this->output."'\n";
// Remove numerical indexes and extension : new URL without output
// extension
$url = "";
foreach ($matches as $key=>$val)
{
if (is_numeric ($key)) continue;
if ($key === "extension") continue;
$url .= $val;
}
if ($this->debug > 1)
echo "After removing extension : url='$url'\n";
}
else
{
$this->output = $this->defaultOutput;
if ($this->debug)
echo "Extension empty found in url : default output '".
"$this->output'\n";
}
}
else
{
$this->output = $this->defaultOutput;
if ($this->debug)
echo "Extension not found in url : default output '$this->output'\n";
}
if ($this->debug > 1)
echo " -- REMOVE EXTENSION TEST IN ROUTE\n";
$route = str_replace ($routeOutput, "", $route);
if ($this->debug)
{
echo "URL='";print_r ($url); echo "'\n";
echo "ROUTE='";print_r ($route); echo "'\n";
}*/
// URL === ROUTE (No variables)
if ($url === $route)
{
if ($this->debug)
{
echo "FOUND : URL === ROUTE (No variables) : ";
echo "Call $class->$method()\n";
echo "==== DEBUG : ROUTING END ====</pre>\n";
}
// For loading user modules controllers
$rc = @include_once ("controllers/controller_$class.php");
// For loading framework classes
if ($rc === FALSE) @include_once ("$class.php");
try
{
$reflection = new ReflectionMethod ($class, $method);
$res = $reflection->invokeArgs(new $class, $params);
}
catch (Exception $e)
{
$res = "ERROR : ". $e->getMessage();
}
return $res;
}
// URL === REGEXP ROUTE
// Variables are exposed in url/{var1}/{var2}
$regex = "#^$route$#U";
$regex = str_replace ("{", "(?P<", $regex);
$regex = str_replace ("}", ">.+)", $regex);
unset ($matches);
$rcRegex = preg_match ($regex, $url, $matches);
if ($rcRegex !== FALSE && $rcRegex !== 0)
{
if ($this->debug)
{
echo "FOUND : URL === REGEX : ";
}
// seach->replace with the matches
$search = array_keys ($matches);
array_walk ($search, function(&$n) { $n = "{".$n."}"; });
$replace = $matches;
// For loading user modules controllers
$rc = @include_once ("controllers/controller_$class.php");
// For loading framework classes
if ($rc === FALSE) $rc = @include_once ("$class.php");
if ($rc === FALSE && $this->debug)
echo "NO MATCH (missing class file)\n";
if ($rc === FALSE)
continue;
// Check if a parameter is for the constructor of the class
$passConstruct = array ();
try
{
// If there is no constructor in the controller, an exception is
// raised
$reflectionClass = new ReflectionMethod ($class, "__construct");
foreach ($reflectionClass->getParameters() as $key=>$param)
{
if (isset ($matches[$param->getName ()]))
$passConstruct[] = $matches[$param->getName ()];
elseif ($param->isOptional ())
$passConstruct[] = $matches[$param->getDefaultValue ()];
else
{
// Missing parameters : abort this route test
if ($this->debug)
echo "NO MATCH (missing constructor parameter)\n";
continue (2);
}
}
}
catch (Exception $e)
{
}
//echo "passConstruct=";print_r ($passConstruct);
// Check if the method need parameters
$reflection = new ReflectionMethod ($class, $method);
$passMethod = array();
//echo "getParameters=";var_dump ($reflection->getParameters());
foreach($reflection->getParameters() as $key=>$param)
{
if (isset ($matches[$param->getName ()]))
$passMethod[] = $matches[$param->getName ()];
elseif (isset ($params[$key]))
$passMethod[] =str_replace ($search, $replace, $params[$key]);
elseif ($param->isOptional ())
$passMethod[] = $matches[$param->getDefaultValue ()];
else
{
if ($this->debug)
echo "NO MATCH (missing method parameter)\n";
// Missing parameters : abort this route test
continue (2);
}
}
if ($this->debug)
{
echo "Call $class(".implode (",", $passConstruct).")->$method(";
echo implode (",", $passMethod).")\n";
echo "==== DEBUG : ROUTING END ====</pre>\n";
}
//echo "matches=";print_r ($matches);
//echo "passMethod=";print_r ($passMethod);
try
{
$refclass = new ReflectionClass ($class);
$refClassInst = $refclass->newInstanceArgs ($passConstruct);
$res = $reflection->invokeArgs ($refClassInst, $passMethod);
}
catch (Exception $e)
{
$res = "ERROR : ". $e->getMessage();
}
return $res;
}
if ($this->debug)
{
echo "NO MATCH\n";
}
}
if ($this->debug)
echo "==== DEBUG : ROUTING END : No route found====</pre>\n";
// TODO : ERROR 404 !
echo "404";
return;
}
}