a/X développement web 2

PHP est le langage de programmation du côté serveur.

Il est souple et mature, dispose d'un nombre impressionnant de bibliothèques de fonctions et est disponible chez tous les hébergeurs.

Il permet la programmation orientée objet, même s'il n'est pas un vrai langage objet comme le Javascript.

Toute la programmation serveur de ce site est en PHP, ainsi que celle de ma bibliothèque HoinP.
Cette bibliothèque, mise dans le domaine public sous licence LGPL, est ma « carte de visite » en matière de programation PHP.

Voir un exemple de classe ci-dessous.

On trouvera d'autres exemples de code au chapitre MVC.


«Copyright 2007 Max Barel a_x@ac-mb.info»
Release : 0.4 ($Rev: 86 $)
$Date: 2014-12-14 21:18:10 +0100 (Dim, 14 déc 2014) $

This file is part of HoP.

HoP is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

HoP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with HoP; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

//XHTML element.
class Hop_element extends Hop_node {
	const INDENT = "\t";		//define the indentation string	
	public static $comment_tag = true;	//optional comment at closing tag for readability. Global to the whole rendering
	public static $pack = false;	//pack the html
	protected static $n_el = 0;	//element count in the pool added for time optimization
	public static $pool;		//a "flat" container object for all Hop_elements.
	//Use it to direct access to an object : Hop_element::$pool->object_id
	//See also the commodity function in Hop class
	//mandatory properties values
	public $type;				//string, element type eg. 'div','p'
	public $id;				//id of this object, automatic generation if not given
	//optional properties
	public $attributes = null;	//stdclass object: html attributes container
	public $auto_id = false;	//should 'id' html attribute be reflecting the object id
	public $auto_name = false;	//should 'name' html attribute be reflecting the object id (eg. for input element)
	public $empty_el = false;	//is this element type empty (otional empty should be set to false)
	public $nl = 1;				//newlines when rendering: 0 for inline (e.g. <span>),
								// 1 for newline before the opening tag (one line elements: <p> or <legend>),
								// 2 for newline before the closing tag (container elements :  <body>, <div>)

	function __construct($type, $options = null) {
		if (!isset(self::$pool)) self::$pool = new stdClass();
		$this->type = $type; // set the element type
		//process options
		if (is_string($options)) $options = array('id' => $options); // a string option default to the object id
		elseif ($options and !is_object($options)) $options = (object) $options; //cast to object for uniform processing
		if (isset($options->content) ) {
			//content should be handled by setter for correct parent linking
			unset ($options->content);

		if ($options) foreach ($options as $k => $v) $this->$k = $v; //make other options properies of the element
		//if (isset($this->$k)) 
		//cast attributes to object for easy access (might be array or simple string)
		if ($this->attributes and !is_object($this->attributes)) $this->attributes = (object) $this->attributes;
		// if ($this->attributes and !is_object($this->attributes)) settype($this->attributes, 'object');

		self::$n_el ++;
		//setting id: if id is specified it's used else a new one is generated in set_id()
		// a side effect is that an existing element with same id becomes unreachable from the pool (in a loop typically).
		// One should previoulsy re-set_id() on such element to be able to reach it.
		// the three line below solve the problem but force to avoid looping on same id.
		// Not enforced yet for compatibility.
		// $id = $this->id;
		// $this->id = null;
		// $this->set_id($id);
	function __clone() {
		self::$n_el ++;
		$id = "{$this->id}_clone_" . self::$n_el;
		$this->id = $this->container = null;
		if ($this->attributes) $this->attributes = clone $this->attributes;
	//id setter. useful for cloned element or changing an automatic id
	function set_id($id = null) {
		if ($this->id) {
			if ($this->container) unset($this->container->{$this->id});
		$this->id = $id;
		//if no id generate one, for the safeness of the pool. The element will then be difficult to address.
		if (!$this->id or !is_string($this->id)) $this->id = $this->type;
		if (isset(self::$pool->{$this->id})) $this->id = "{$this->type}_" . self::$n_el;
		//add self to the pool
		self::$pool->{$this->id} = $this;
		if ($this->container) $this->container->{$this->id} = $this;
		return $this;

	protected function set_container($node) {
		//redefined to add a reverse link from parent to child through an id named property
		parent::set_container($node); //=> $this->container = $node;
		// if (is_a($node, __CLASS__))
		$node->{$this->id} = $this; //cannot be done at node level becaus nodes have no id
	function cut() {
		return parent::cut();
	//rendering function.
	function render($indent = '') {
		$nl = self::$pack ? 0 : $this->nl;
		$html_string = '';
		if ($this->auto_id and empty($this->attributes->id) ) $this->set_attr('id', $this->id);
		if ($this->auto_name and empty($this->attributes->name) ) $this->set_attr('name', $this->id);
		if ($nl) $html_string .= "\n$indent";
		$html_string .= "<$this->type";
		if ($this->attributes) {
			//cast in case it has been set to a string
			if (!is_object($this->attributes)) $this->attributes = (object) $this->attributes;
			foreach ($this->attributes as $k => $value) {
				if ($k == 'raw' or $k == 'scalar') $html_string .= " $value"; //for raw formatted attributes
				else $html_string .= " $k=\"$value\"";
		if ($this->empty_el) {
			$html_string .= ' />';
		} else {
			$html_string .= '>';
			$html_string .=  parent::render(self::INDENT . $indent);
			if ($nl > 1) $html_string .= "\n$indent";
			$html_string .= "</$this->type>";
			//optional, for code documentation
			if ($nl > 1 and self::$comment_tag and isset($this->attributes->id)) $html_string .= "<!-- {$this->attributes->id} -->";
		return $html_string;

	//properties setter
	function auto_id($val=true) { $this->auto_id = $val; return $this; }
	function auto_name($val=true) { $this->auto_name = $val; return $this; }
	function empty_el($val=true) { $this->empty_el = $val; return $this; }
	function nl($val=1) { $this->nl = $val; return $this; }
	//commodity setter for html id
	function id($value = null) {
		if ($value) $this->set_attr('id', $value); //set html id attribute, no incidence on hop id
		else $this->auto_id = true; // shortcut to use hop id
		return $this;
	//commodity setter for html name
	function name($value = null) {
		if ($value) $this->set_attr('name', $value); //set html name attribute
		else $this->auto_name = true; // shortcut to <auto_name></auto_name>
		return $this;
	//commodity function to set an attribute
	function set_attr($attr, $value=null) {
		if (!isset($this->attributes)) $this->attributes = new stdClass();
		if (isset($value)) $this->attributes->$attr = htmlspecialchars($value); //htmlspecialchars htmlentities
		return $this;
	//commodity function to get an attribute
	function get_attr($attr) {
		return @$this->attributes->$attr;
	//remove an attribute
	function remove_attr($attr) {
		unset ($this->attributes->$attr);
		return $this;

	//generic attibute setter
	public function __call($setter, $params) {
		if (@ereg('(set|get|remove)_(.*)', $setter, $e_res)) {
			switch ($e_res[1]) {
				case 'set': return $this->set_attr($e_res[2], $params[0]);
				case 'get': return $this->get_attr($e_res[2]);
				case 'remove': return $this->remove_attr($e_res[2]);
		//shortcut setter: $el->class('a_class') <=> $el->set_class('a_class') <=> $el->set_attr('class', 'a_class')
		else return $this->set_attr($setter, $params[0]);
		// else throw new Exception("invalide function call : $setter");

	//specific class methods
	public function add_class($class = null) {
		if ($class) $this->set_attr('class', (($c = $this->get_attr('class')) ? "$c " : '') . "$class");
		return $this;
	public function remove_class($class = null) {
		if ($class) $this->set_attr('class', trim(@ereg_replace(" *$class", '', $this->get_attr('class'))));
		return $this;


Valid XHTML 1.0 Strict