Expertise (5/5)
J'utilise PHP depuis plus de 15 ans. Initialement, je développais des services WEB qui s'intégraient au sein de grands systèmes d'information. Puis, progressivement, pour des besoins personnels, j'ai utilisé le framework ZEND (développement de sites pour des particuliers). J'ai aussi développé (en PHP) de nombreux greffons pour le moteur de réseau social ELGG, ainsi que pour le CMS Joomla.
Quelques packages Composer .
J'ai écrit, il y a quelques années déjà, des modules pour PHP4. Concrètement, il s'agit de librairies dynamiquement changeables. À l'époque, j'ai développé, en particulier, un module qui implémentait un algorithme de chiffrage particulier. De nos jours, il n'est guère nécessaire de développer ses propres modules.
Généralités
L'exemple de code ci-dessous présente un module très simple qui exporte une fonction. Cette fonction effectue la somme des deux arguments qui lui sont fournis en argument.
Déclaration du module (newfunc.h)
Implémentation du module (newfunc.c)
#include
#include "php.h"
#include "php_ini.h"
#include "newfunc.h"
function_entry newfunc_functions_entry[] =
{
PHP_FE(newfunction1, NULL)
{NULL, NULL, NULL}
};
zend_module_entry newfunc_module_entry =
{
"newfunc", /* name */
newfunc_functions_entry, /* function_entry */
NULL, /* initializator */
NULL, /* destructor */
NULL, /* startup */
NULL, /* shutdown */
NULL, /* info */
STANDARD_MODULE_PROPERTIES
};
DLEXPORT zend_module_entry *get_module(void) { return &newfunc_module_entry; }
PHP_FUNCTION(newfunction1)
{
pval *arg1, *arg2;
/************************************************************************/
/* Make sure that we have two arguments */
/************************************************************************/
if (ARG_COUNT(ht) != 2) { WRONG_PARAM_COUNT; }
/************************************************************************/
/* Get the two arguments from the arguments' list */
/************************************************************************/
if (getParameters(ht,2,&arg1,&arg2)==FAILURE) { WRONG_PARAM_COUNT; }
/************************************************************************/
/* Make sure we have 'long' values */
/************************************************************************/
convert_to_long(arg1);
convert_to_long(arg2);
/************************************************************************/
/* Calculate arg1 + arg2 */
/************************************************************************/
RETURN_LONG(arg1->value.lval + arg2->value.lval);
}
Makefile
#########################################################
# Generic Makefile for PHP dinamically loadable modules #
#########################################################
CC = gcc
#########################################################
# Where to put PHP's dinamically loadable modules #
#########################################################
PHP_MODULE_PATH = /home/php4/modules
#########################################################
# Where to fing PHP's header files #
#########################################################
PHP_TOP = /home/php4/include/php
PHP_TSRM = ${PHP_TOP}/TSRM
PHP_ZEND = ${PHP_TOP}/Zend
PHP_EXT = ${PHP_TOP}/ext
PHP_MAIN = ${PHP_TOP}/main
PHP_REGEX = ${PHP_TOP}/regex
#########################################################
# Compiler's options #
#########################################################
CFLAGS = -I${PHP_TOP} \
-I${PHP_TSRM} \
-I${PHP_ZEND} \
-I${PHP_EXT} \
-I${PHP_MAIN} \
-I${PHP_REGEX} \
-DCOMPILE_DL
# -shared
# Produce a shared object which can then be linked with other objects to form an executable. Only a
# few systems support this option.
# -fPIC
# If supported for the target machine, emit position-independent code, suitable for dynamic linking,
# even if branches need large displacements.
CFLAGSSO = -shared -fPIC -ldl
newfunc.o: newfunc.c \
newfunc.h
${CC} ${CFLAGS} -c newfunc.c
newfunc.so: newfunc.o
${CC} ${CFLAGSSO} -o newfunc.so newfunc.o
all: newfunc.so
install: newfunc.so
cp newfunc.so ${PHP_MODULE_PATH}/
clean:
rm newfunc.o newfunc.so
HTML
Test for the dunamically loadable module newfunc ";
dl("newfunc.so");
$a=1;
$b=2;
print " a = $a";
print " b = $b";
$c = newfunction1($a,$b);
print " c = newfunction1(a,b) = $c";
?>
Résultat
test for the dunamically loadable module newfunc
a = 1
b = 2
c = newfunction1(a,b) = 3
Passage des paramètres de type scalaire
Le code ci-dessous illustre le fait que les arguments de la (nouvelle) fonction PHP sont passés par valeur, et non par adresse.
#include
#include "php.h"
#include "php_ini.h"
#include "newfunc.h"
function_entry newfunc_functions_entry[] =
{
PHP_FE(newfunction1, NULL)
{NULL, NULL, NULL}
};
zend_module_entry newfunc_module_entry =
{
"newfunc", /* name */
newfunc_functions_entry, /* function_entry */
NULL, /* initializator */
NULL, /* destructor */
NULL, /* startup */
NULL, /* shutdown */
NULL, /* info */
STANDARD_MODULE_PROPERTIES
};
DLEXPORT zend_module_entry *get_module(void) { return &newfunc_module_entry; }
PHP_FUNCTION(newfunction1)
{
pval *arg1, *arg2;
long resultat;
/************************************************************************/
/* Make sure that we have two arguments */
/************************************************************************/
if (ARG_COUNT(ht) != 2) { WRONG_PARAM_COUNT; }
/************************************************************************/
/* Get the two arguments from the arguments' list */
/************************************************************************/
if (getParameters(ht,2,&arg1,&arg2)==FAILURE) { WRONG_PARAM_COUNT; }
/************************************************************************/
/* Make sure we have 'long' values */
/************************************************************************/
convert_to_long(arg1);
convert_to_long(arg2);
/************************************************************************/
/* Calculate arg1 + arg2 */
/************************************************************************/
resultat = arg1->value.lval + arg2->value.lval;
/* Focus on the following line: */
arg1->value.lval *= 10;
RETURN_LONG(resultat);
}
ou (code équivalent)
#include
#include "php.h"
#include "php_ini.h"
#include "newfunc.h"
...
PHP_FUNCTION(newfunction1)
{
pval **arg1, **arg2;
long resultat;
/************************************************************************/
/* Make sure that we have two arguments */
/************************************************************************/
if (ARG_COUNT(ht) != 2) { WRONG_PARAM_COUNT; }
/************************************************************************/
/* Get the two arguments from the arguments' list */
/************************************************************************/
if (getParameters(ht,2,arg1,arg2)==FAILURE) { WRONG_PARAM_COUNT; }
/************************************************************************/
/* Make sure we have 'long' values */
/************************************************************************/
convert_to_long(*arg1);
convert_to_long(*arg2);
/************************************************************************/
/* Calculate arg1 + arg2 */
/************************************************************************/
resultat = (*arg1)->value.lval + (*arg2)->value.lval;
(*arg1)->value.lval *= 10;
RETURN_LONG(resultat);
}
HTML
Test for the dunamically loadable module newfunc ";
dl("newfunc.so");
$a=1;
$b=2;
print " a = $a";
print " b = $b";
$c = newfunction1($a,$b);
print " c = newfunction1(a,b) = $c";
print " a = $a";
?>
Résultat. Remarquez que la valeur de la variable "a" n'a pas changé.
test for the dunamically loadable module newfunc
a = 1
b = 2
c = newfunction1(a,b) = 3
a = 1
Utilisation des chaînes de caractères
Documentation :
One thing you must know is that ALL string returned by a PHP function must have been allocated using emalloc(). PHP will free it (using "efree"), so if you did not use emalloc ... you are in big trouble.
There are two kinds of memory in this program. Memory which is returned to the parser in a variable and memory which you need for temporary storage in your internal function. When you assign a string to a variable which is returned to the parser you need to make sure you first allocate the memory with either emalloc or estrdup. This memory should NEVER be freed by you, unless you later, in the same function overwrite your original assignment (this kind of programming practice is not good though).
For any temporary/permanent memory you need in your functions/library you should use the three emalloc(), estrdup(), and efree() functions. They behave EXACTLY like their counterpart functions. Anything you emalloc() or estrdup() you have to efree() at some point or another, unless it's supposed to stick around until the end of the program, otherwise there will be a memory leak. The meaning of "the functions behave exactly like their counterparts" is if you efree() something which was not emalloc()'ed nor estrdup()'ed you might get a segmentation fault. So please take care and free all of your wasted memory. One of the biggest improvements in PHP 3.0 will hopefully be the memory management.
Implémentation du module (newfunc.c)
#include
#include "php.h"
#include "php_ini.h"
#include "newfunc.h"
...
char *My_BIN2HEX (char*, const int, int*);
PHP_FUNCTION(newfunction1)
{
pval *string;
char *result;
int new_length;
/************************************************************************/
/* Make sure that we have two arguments */
/************************************************************************/
if (ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
/* or "(ARG_COUNT() != 1) { WRONG_PARAM_COUNT; }" */
/************************************************************************/
/* Get the two arguments from the arguments' list */
/************************************************************************/
if (getParameters(ht,1,&string)==FAILURE) { WRONG_PARAM_COUNT; }
/************************************************************************/
/* Make sure we have a characters' string */
/************************************************************************/
convert_to_string(string);
/* or "convert_to_string_ex(&string);" */
result = My_BIN2HEX (string->value.str.val, string->value.str.len, &new_length);
if (result == NULL) { RETURN_FALSE; }
/* Look at the file "Zend/zend_API.h" to get the list of RETURN_XXX macros */
/* Here we don't duplicate the resulting string (third arg is 0) since we */
/* have allocated "result" using "emalloc". */
RETURN_STRINGL (result, new_length, 0);
}
char *My_BIN2HEX (char *src, const int length, int *new_length)
{
int i;
char *result, aux[3];
/************************************************************************/
/* Use "emalloc()" instead of "malloc()" */
/* Remember that under PHP, resulting strings MUST be allocated by */
/* "emalloc" --- Do NOT use statically allocated strings because PHP */
/* will free it. */
/************************************************************************/
result = (char*)emalloc(2*length*sizeof(char));
if (result == NULL) { return NULL; }
for (i=0; i
HTML
Test for the dunamically loadable module newfunc ";
dl("newfunc.so");
$a=1;
$b=2;
print " a = $a";
print " b = $b";
$c = newfunction1($a);
print " c = newfunction1(\"$a\") = $c";
$c = newfunction1("ABCDEFGH");
print " c = newfunction1(\"ABCDEFGH\") = $c";
?>
Résultat
Test for the dunamically loadable module newfunc
a = 1
b = 2
c = newfunction1("1") = 31
c = newfunction1("ABCDEFGH") = 4142434445464748
Il arrive que vous deviez convertir une série de fichiers en UTF8. Le script Perl suivant effectue cette tâche. Il convertit tous les fichiers PHP présents dans un répertoire donné en UTF8.
use strict;
use Encode;
use Encode::Guess;
use Getopt::Long;
use Data::Uniqid qw(suniqid uniqid luniqid);
use File::Temp qw(tmpnam);
use File::Slurp;
my $optDirectory = undef;
my $help = undef;
my @files = ();
my @templates = ();
# -------------------------------------------------------------------------------
# Parsing the command line.
# -------------------------------------------------------------------------------
unless (
GetOptions (
'directory=s' => \$optDirectory,
'help' => \$help,
)
)
{
print STDERR "ERROR: Invalid command line. Use option --help to get help.\n";
exit 1;
}
if (defined($help)) { help(); exit 0; }
$optDirectory = defined($optDirectory) ? $optDirectory : '.';
# -------------------------------------------------------------------------------
# Get the list of all files in the directory.
# -------------------------------------------------------------------------------
unless (opendir (FDDIR, "$optDirectory")) { print STDERR "ERROR: Can not open directory <$optDirectory>: $!\n"; exit 1; }
@files = readdir(FDDIR);
foreach my $file (@files) { if ($file =~ m/^.+\.php$/i) { push (@templates, $file); } }
closedir (FDDIR);
# -------------------------------------------------------------------------------
# Convert all templates into UYF8, without BOM.
# -------------------------------------------------------------------------------
foreach my $file (@templates)
{
my $input = undef; # Path to the iput file.
my $fileContent = undef; # Content of the file to convert into UTF8, without BOM.
my $decoder = undef; # Charset's decoder.
my $data = undef; # This string represents the content of the file to convert into Perl's internal representation.
my $convertedContent = undef; # This string represents the content of the file, converted into UTF8, without BOM.
my $start = undef; # This string represents the three first bytes of the UTF8 converted string (that is $convertedContent).
my $toSave = undef; # This string represents the string that will be written into the target file.
my $tmpFile = undef; # Path to a temporary file.
my $rv = undef;
print STDOUT "Converting file $optDirectory/$file\n";
$input = $optDirectory . '/' . $file;
$fileContent = read_file($input, binmode => ':raw');
unless(defined($fileContent)) { print STDERR "ERROR: Could not read template file <$fileContent>!\n"; exit 1; }
$decoder = guess_encoding($fileContent, 'latin1');
unless (ref($decoder)) { $decoder = guess_encoding($fileContent); }
unless (ref($decoder)) { print STDERR "ERROR: Could not guess file encoding!"; exit 1; }
$data = $decoder->decode($fileContent);
$convertedContent = encode('utf-8', $data);
$start = length($convertedContent) >= 3 ? unpack('H6', substr($convertedContent, 0, 3)) : undef;
$toSave = $start eq 'efbbbf' ? substr($convertedContent, 3) : $convertedContent;
$tmpFile = tmpnam();
print STDOUT "\tTemporary file: $tmpFile\n";
$rv = write_file($tmpFile, {binmode => ':raw'}, $toSave);
unless(defined($rv)) { print STDERR "ERROR: Could not write data into file <$tmpFile>.\n"; exit 1; }
if (1 != unlink($input)) { print STDERR "ERROR: Could not remove file <$input>: $!\n"; }
unless (rename($tmpFile, $input)) { print STDERR "ERROR: Could not rename file <$tmpFile> into <$input>: $!\n"; exit 1; }
unlink ($tmpFile);
chmod (0666, $input);
}
print STDOUT "done\n";
exit 0;
sub help
{
print STDOUT "Usage: perl utf8.pl [--directory=] [--help]\n";
}
L'envoi de fichier vers un serveur peut parfois dérouter les débutants... Ce code très simple illustre la procédure à suivre.
PHP simple
Envoyez ce fichier :
');
echo htmlentities("Le code d'erreur est $downloadErrorCode");
exit (0);
}
$originalFileName = basename($_FILES['userfile']['name']);
$originalFileSize = $_FILES['userfile']['size'];
$temporaryFileName = $_FILES['userfile']['tmp_name'];
$targetPath = "$TARGETDIR/$originalFileName";
if (! rename($temporaryFileName, $targetPath))
{
echo htmlentities("Le téléchargement de votre fichier a échoué.");
echo (' ');
echo htmlentities("Impossible de renommer le fichier $temporaryFileName en $targetPath.");
exit (0);
}
echo htmlentities("Le téléchargement de votre fichier a réussi.");
}
?>
Framework ZEND
addFilter('Rename', '/tmp/upload/');
// --------------------------------------------------------------------------------
// Définition des validateurs.
// http://framework.zend.com/manual/fr/zend.file.transfer.validators.html
// --------------------------------------------------------------------------------
// Limitation de la taille maximum du fichier chargé.
// La valeur du deuxième argument signifie que le chargement s'interrompt si la taille du fichier de respecte pas les limites imposées.
$adapter->addValidator('Size', true, array('min' => 20, 'max' => 2000000));
// Si le fichier téléchargé existe déjà, alors on annule le chargement.
// La valeur du deuxième argument signifie que le chargement s'interrompt si la taille du fichier de respecte pas les limites imposées.
// L'activation du filtre "Rename" rend cette validation inutile.
$adapter->addValidator('NotExists', true, '/tmp');
// Le serveur n'accepte que les images.
$adapter->addValidator('IsImage', true, array('image/gif', 'image/png', 'image/jpeg'));
// Chargement des fichiers.
if (FALSE === $adapter->receive())
{
echo 'ERROR:';
echo '';
foreach ($adapter->getMessages() as $message) { echo '' . $message . " \n"; }
echo ' ';
echo ' ';
exit;
}
// Extraction des informations relatives au(x) fichier(s) reçu(s).
$files = $adapter->getFileInfo();
echo '' . var_dump_html($files) . " \n";
// Nombre de fichiers téléchargés.
echo "Number of uploaded files: " . count($files) . " ";
// Le fichier désigné par le "nom de transfert" "uploadedfile1" a-t-il été reçu?
if ($adapter->isUploaded('uploadedfile1'))
{ echo 'uploadedfile1 is uploaded. '; }
else { echo 'uploadedfile1 is not uploaded. '; }
// Affichage des informations relatives au fichier téléchargé.
echo '';
if ($adapter->isUploaded('uploadedfile1'))
{
echo 'Path to the temporary directory on the server used to store the received file: ' . $adapter->getDestination('uploadedfile1') . ' ';
echo 'Name of the file: ' . $adapter->getFileName('uploadedfile1', false) . ' ';
echo 'Full path to the file, on the server: ' . $adapter->getFileName('uploadedfile1', true) . ' ';
echo 'Type: ' . $adapter->getMimeType('uploadedfile1') . ' ';
$adapter->setOptions(array('useByteString' => false));
echo 'Size of the file, in bytes: ' . $adapter->getFileSize('uploadedfile1') . ' ';
$adapter->setOptions(array('useByteString' => true));
echo 'Size of the file, human readable: ' . $adapter->getFileSize('uploadedfile1') . ' ';
}
echo ' ';
}
catch (Exception $e)
{
echo "ERROR: " . "Fichier: " . $e->getFile() . " ligne: " . $e->getLine() . ' : ' . $e->getMessage() . "\n";
}
// On manipule le fichier...
function var_dump_html ($inVar)
{
$txt = null;
ob_start();
var_dump($inVar);
$txt = ob_get_contents();
ob_end_clean();
return $txt;
}
echo 'OK';
?>
HTML
Choose a file to upload:
Choose a file to upload:
"Active l'affiche d'information sur le déroulement du scripte.",
'level|l=i' => "Niveau de verbosité (de 1 à 5) (obligatoire).",
'host|h=s' => "Nom de l'hôte ou adresse IP (obligatoire).",
'alert|a-s' => "Lance une alerte en cas d'erreur. Cette option peut, éventuellement, être suivie d'une adresse email."
);
try { $opts = new Zend_Console_Getopt($cliArgs);
$opts->parse(); }
catch (Zend_Console_Getopt_Exception $e) { echo $e->getUsageMessage(); exit (1); }
// Extraction des options.
if (isset($opts->v)) { $verbose = TRUE; }
if (isset($opts->level)) { $level = $opts->getOption('level'); }
if (isset($opts->host)) { $host = $opts->getOption('host'); }
if (isset($opts->alert)) { $alert = $opts->getOption('alert'); }
// Affichage des options.
$options = array ('verbose' => $verbose ? 'TRUE' : 'FALSE', 'level' => $level, 'host' => $host, 'alert' => $alert);
foreach ($options as $key => $value) { echo "$key => $value\n"; }
// Liste des arguments qui ne sont pas des options.
$args = $opts->getRemainingArgs();
echo "\n";
echo "Liste des arguments:\n";
foreach ($args as $arg) { echo "- $arg\n"; }
}
catch (Exception $e)
{
echo ("ERREUR: " . $e->getMessage() . ' Line ' . $e->getLine() . "\n");
exit (1);
}
exit (0);
?>
Exemple
Tables MySql
mysql> desc menu;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| idmenu | int(11) | NO | PRI | NULL | auto_increment |
| entree | varchar(45) | YES | | NULL | |
| plat | varchar(45) | YES | | NULL | |
| dessert | varchar(45) | YES | | NULL | |
| date | datetime | NO | | NULL | |
+---------+-------------+------+-----+---------+----------------+
mysql> desc `table`;
+---------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+----------------+
| idtable | int(11) | NO | PRI | NULL | auto_increment |
| numero | int(11) | NO | | NULL | |
+---------+---------+------+-----+---------+----------------+
mysql> desc commande;
+------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+----------------+
| idcommande | int(11) | NO | PRI | NULL | auto_increment |
| fk_menu | int(11) | NO | | NULL | |
| fk_table | int(11) | NO | | NULL | |
+------------+---------+------+-----+---------+----------------+
La figure ci-dessous représente les relations entre les tables.
Fichier de configuration
; Configuration pour l'environnement de production.
; myDataBase.database.adapter: Adaptateur SQL.
; myDataBase.database.params.dbname: Nom de la base de données.
; myDataBase.database.params.host: Adresse du serveur MySql.
; myDataBase.database.params.username: Login.
; myDataBase.database.params.password: Mot de passe.
[PROD]
myDataBase.database.adapter = pdo_mysql
myDataBase.database.params.dbname = restau
myDataBase.database.params.host = localhost
myDataBase.database.params.username = serveur
myDataBase.database.params.password = toto
[DEV : PROD]
; Configuration pour l'environnement de développement.
; Cet environnement hérite des valeurs de l'environnement de production.
myDataBase.database.params.host = localhost
myDataBase.database.params.username = serveur
myDataBase.database.params.password = toto
Initialisation des tables
myDataBase->database);
Zend_Db_Table::setDefaultAdapter($db);
$menu = new Zend_Db_Table('menu');
$table = new Zend_Db_Table('table');
$commande = new Zend_Db_Table('commande');
// Insertion de données des menus.
$data = array (
'entree' => 'salade',
'plat' => 'poulet',
'dessert' => 'tarte',
'date' => new Zend_Db_Expr('NOW()')
);
$menu->insert($data);
$menus[] = $menu->getAdapter()->lastInsertId();
$data = array (
'entree' => 'verine',
'plat' => 'boeuf',
'dessert' => 'glace',
'date' => new Zend_Db_Expr('NOW()')
);
$menu->insert($data);
$menus[] = $menu->getAdapter()->lastInsertId();
$data = array (
'entree' => 'avocat',
'plat' => 'poisson',
'dessert' => 'fruits',
'date' => new Zend_Db_Expr('NOW()')
);
$menu->insert($data);
$menus[] = $menu->getAdapter()->lastInsertId();
// Attribution des numéros des tables.
for ($i=1; $i<=20; $i++) { $data = array( 'numero' => $i ); $table->insert($data); $tables[] = $table->getAdapter()->lastInsertId(); }
// Simulation de prises de commandes.
$meniId = 0;
for ($i=0; $i<20; $i++)
{
echo "La table " . $tables[$i] . ' commande le menu ' . $menus[$meniId] . "\n";
$data = array( 'fk_menu' => $menus[$meniId], 'fk_table' => $tables[$i] );
$commande->insert($data);
$meniId = ($meniId + 1) % count($menus);
}
// Fermeture de la connexion avec la base de donnée.
$db->closeConnection();
}
catch (Exception $e)
{
echo "ERROR: " . "Fichier: " . $e->getFile() . " ligne: " . $e->getLine() . ' : ' . $e->getMessage() . "\n";
if (! is_null($db)) { $db->closeConnection(); }
}
?>
Utilisation
getFile() . " ligne: " . $e->getLine() . ' : ' . $e->getMessage() . "\n"; exit (1); }
// Définitions de la table "menu".
// Les valeurs d'une colonne de la table "commande" dépendent des valeurs d'une colonne de la table "menu".
// En l'occurence : commande.fk_menu dépend de menu.idmenu.
// On déclare donc que la table "commande" est dépendante de la table courante (en l'occurence "menu").
// => $_dependentTables
class Menu extends Zend_Db_Table_Abstract
{
protected $_schema = 'restau'; // Nom de la base (optionnel).
protected $_name = 'menu'; // Nom de la table (optionnel: par défaut, le nom de la classe).
protected $_primary = 'idmenu'; // Nom de la clé primaire.
protected $_dependentTables = array('commande');
};
// Définitions de la table "table".
class Table extends Zend_Db_Table_Abstract
{
protected $_schema = 'restau'; // Nom de la base (optionnel).
protected $_name = 'table'; // Nom de la table (optionnel: par défaut, le nom de la classe).
protected $_primary = 'idtable'; // Nom de la clé primaire.
protected $_dependentTables = array('commande');
};
// Définitions de la table "commande".
// Une commande est associée à une table et à un menu.
// L'association est symbolisée par les liens vers les tables "menu" et "table".
// Il faut définir les liens vers les deux tables dont dépend cette table.
// => $_referenceMap est défini.
class Commande extends Zend_Db_Table_Abstract
{
protected $_schema = 'restau'; // Nom de la base (optionnel).
protected $_name = 'commande'; // Nom de la table (optionnel: par défaut, le nom de la classe).
protected $_primary = 'idcommande'; // Nom de la clé primaire.
// Définitions des liens avec les autres tables.
protected $_referenceMap = array (
// Lien vers le menu.
// 'menuLink' est le nom du lien (choisi arbitrairement).
'menuLink' =>
array (
'columns' => 'fk_menu', // Nom de le colonne dans la table "commandes".
'refTableClass' => 'Menu', // Nom de la *classe* de la table vers laquelle pointe le lien.
'refColumns' => 'idmenu' // Nom de la colonne dans la table de destination du lien.
),
// Lien vers la table.
// Un lien peut être représenté par une association "N:N" (pas nécessairement "1:1").
// C'est la raison pour laquelle les attributs 'columns' et 'refColumns' peuvent se voir attribuer des tableaux.
'tableLink' =>
array (
'columns' => array('fk_table'),
'refTableClass' => 'Table',
'refColumns' => array('idtable')
)
);
};
try {
$allTables = null;
// Chargement de la configuration externe.
$conf = new Zend_Config_Ini(CONFPATH, ENVPATH);
// Connexion à la base de donnée et branchement sur la table des menus.
$db = Zend_Db::factory($conf->myDataBase->database);
Zend_Db_Table_Abstract::setDefaultAdapter($db);
// Connexions des tables.
$commande = new Commande();
$menu = new Menu();
$table = new Table();
// Sélection de toutes les tables.
$allTables = $table->fetchAll($table->select());
echo "Nombre de tables: " . count($allTables) . "\n";
// Pour chaque table, on affiche la commande et le menu.
foreach ($allTables as $oneTable)
{
echo "Table: " . $oneTable->idtable . "\n";
// Sélection depuis une table "de référence", vers une table "dépendante".
// Note: Le premier argument de la méthode "findDependentRowset()" représente le nom de la *classe* (et non celui de la table).
// Si plusieurs liens existent entre les deux tables sur lesquelles s'effectue l'opération, il est nécessaire de spécifier le nom du lien à suivre (exemple: "menuLink").
// Il est possible d'appliquer un filtre de sélection via le troisième argument optionnel.
// Optionnellement: $tableCommandRow = $oneTable->findDependentRowset('Commande', 'tableLink');
// Optionnellement: $tableCommandRow = $oneTable->findDependentRowset('Commande', 'tableLink', $select);
$tableCommandRow = $oneTable->findDependentRowset('Commande');
echo "\tidcommande: " . $tableCommandRow->current()->idcommande . "\n";
// Sélection depuis une table "dépendante", vers une table "de référence".
// Note: Le premier argument de la méthode "findParentRow()" représente le nom de la *classe* (et non celui de la table).
$tableMenuRow = $tableCommandRow->current()->findParentRow('Menu');
echo "\tidmenu = " . $tableMenuRow->idmenu . "\n";
echo "\tentree = " . $tableMenuRow->entree . "\n";
echo "\tplat = " . $tableMenuRow->plat . "\n";
echo "\tdessert = " . $tableMenuRow->dessert . "\n";
echo "\tdate = " . $tableMenuRow->date . "\n";
}
// Fermeture de la connexion avec la base de donnée.
$db->closeConnection();
}
catch (Exception $e)
{
echo "ERROR: " . "Fichier: " . $e->getFile() . " ligne: " . $e->getLine() . ' : ' . $e->getMessage() . "\n";
if (! is_null($db)) { $db->closeConnection(); }
}
?>
Tableau
resources.myResourceName.myResourceValue.0 = ...
resources.myResourceName.myResourceValue.1 = ...
resources.myResourceName.myResourceValue.2 = ...
...
Table de hachage (ou dictionnaire)
resources.myResourceName.myResourceValue.key1 = value1
resources.myResourceName.myResourceValue.key2 = value2
resources.myResourceName.myResourceValue.key3 = value3
...
Si l'on ne tient pas compte de l'utilisation éventuelle de la classe d'amorçage ("Bootstrap.php"), une ressource est initialisée seulement si elle apparaît dans le fichier d'initialisation (".ini"). Ainsi, pour initialiser la vue, il faut insérer la ligne dans le fichier d'initialisation de l'application :
resources.view =
Dans le fichier d'initialisation de l'application, les délimiteurs de chaîne ne sont pas interchangeables.
Il est conseillé d'utiliser le guillemet double (").
La ligne ci-dessous produit le résultat escompté.
APPLICATION_PATH "/../logs/application.log"
La ligne ci-dessous génère l'insertion d'un espace indésirable à l'endroit de la concaténation.
APPLICATION_PATH '/../logs/application.log'
# Ligne ajoutée
SetEnv APPLICATION_ENV development
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
# RewriteRule !\.(js|ico|gif|jpg|css|htm|jpeg|jgz|swf)$ index.php
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
La configuration du chargeur de classe peut se faire à deux endroits :
Dans le fichier d'initialisation de l'application.
Au niveau du point d'entrée de l'application. En général, il s'agit du fichier "index.php" (sauf si les règles de réécriture d'URL définies dans le fichier ".htaccess" mentionnent un autre fichier).
Dans les exemples qui suivent, les classes dont le nom commence par les préfixes"My_" et "Libs_" seront automatiquement chargées lors de leur utilisation.
La classe My_Example_Class doit être stockée dans le fichier "My/Example/Class.php".
La classe Libs_Example_Class doit être stockée dans le fichier "Libs/Example/Class.php".
Dans le fichier d'initialisation de l'application
autoloaderNamespaces.0 = "My"
autoloaderNamespaces.1 = "Libs"
...
Au niveau du point d'entrée de l'application
// Déclaration d'un espace de nom pour les classes.
Zend_Loader_Autoloader::getInstance()->registerNamespace('My_');
Zend_Loader_Autoloader::getInstance()->registerNamespace('Libs_');
Le code ci-dessous déclare un chargeur automatique de ressources.
On commence par déclarer "l'espace de nom" (namespace) pour les ressources.
En l'occurrence, on déclare l'espace de nom désigné par la chaîne "Application".
Puis on déclare des types de ressource pour l'espace de nom précédemment déclaré.
En l'occurrence, on déclare le type de ressource désigné par le nom "model".
// Déclaration de l'espace de nom **des ressources** applicatives.
// On déclare le chemin vers une ressource, désignée par son espace de nom.
// En l'occurrence : La ressource, désignée par l'espace de nom "Application", est stockée dans le répertoire "APPLICATION_PATH".
// NOTE: Les autoloaders de ressources s'enregistrent dans l'autoloader à leur instanciation.
// http://framework.zend.com/manual/fr/zend.loader.autoloader-resource.html
// NOTE: Une classe qui commence par "Application_" sera considérée comme une "ressource applicative".
$resourceLoader = new Zend_Loader_Autoloader_Resource( array( 'namespace' => 'Application', 'basePath' => APPLICATION_PATH));
// On déclare un "type" de "ressource applicative" (le type "model", en l'occurence).
// En l'occurrence, les ressources de type "model", sont stockées dans le sous-répertoire "model/" du répertoire des "ressources applicatives", soit:
// APPLICATION_PATH . DIRECTORY_SEPARATOR . 'models'.
// Et les noms des classes qui implémentent ce type de "ressource applicative" commencent par : "Application_Model"
// First arg: An arbitrary name, that identifies the type of resource.
// Second arg : The subdirectory, within the resource directory, where to store all classes.
// Third arg : The classes' prefix.
$resourceLoader->addResourceType('model', 'models/', 'Model');
L'aide d'action (Fichier: My/ActionsHelpers/MyHelper.php).
class My_ActionsHelpers_MyHelper extends Zend_Controller_Action_Helper_Abstract
{
public function direct(...) { }
private function __getBootstrap() { return $this->getFrontController()->getParam('bootstrap'); }
private function __getResource($inName) { return $this->__getBootstrap()->getResource($inName); }
}
Configuration du chargeur d'aides d'action.
// Juste avant la ligne "$application->bootstrap()->run();"!!!!
Zend_Controller_Action_HelperBroker::addPrefix('My_ActionsHelpers_');
$application->bootstrap()->run();
Utilisation de l'aide d'action dans le contrôleur.
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->_helper->MyHelper(...);
}
...
}
Dans l'exemple qui suit, on initialise la ressource "session" à l'intérieur de la classe d'amorçage de l'application.
Il est aussi possible d'initialiser cette ressource dans le fichier d'initialisation de l'application.
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initSession()
{
Zend_Session::start();
$session = new Zend_Session_Namespace('user', true);
return $session;
// http://framework.zend.com/manual/fr/zend.application.theory-of-operation.html
// Pour initialiser la ressource depuis une autre méthode du fichier "Bootstrap.php".
// $this->bootstrap('session');
// Pour accéder à la session depuis une autre méthode du fichier "Bootstrap.php":
// $bootstrap->getResource('session');
}
protected function _initRessource()
{
$this->bootstrap('session');
$session = $bootstrap->getResource('session');
}
}
Création d'une ressource personnalisée
Il est possible de définir ses propres ressources, implémentées sous la forme de classes.
Dans l'exemple ci-dessous :
Le greffon de ressource est implémenté par la classe "My_Resources_Googleajaxsearchapikey
".
La classe est définie dans le fichier "File: My/Resources/Googleajaxsearchapikey.php
".
Le nom de la ressource (utilisé dans le fichier d'initialisation de l'application) est défini par la propriété "$_explicitType
", de la classe la classe "My_Resources_Googleajaxsearchapikey
".
ATTENTION !!! Le nom du fichier PHP doit se composer uniquement de caractères minuscules, sauf le premier caractère!
class My_Resources_Googleajaxsearchapikey extends Zend_Application_Resource_ResourceAbstract
{
const DEFAULT_REGISTRY_KEY = 'googleAjaxSearchApiKey';
protected $_key = null;
/** Cette propriété définit, explicitement, le nom de la ressource ($bootstrap->bootstrap(); $rcs = $bootstrap->getResource();).
*/
public $_explicitType = 'googleAjaxSearchApiKey';
/** Initialise la ressource.
* @return La méthode retourne la "valeur" de la ressource.
*/
public function init()
{
// La valeur de la ressource sera automatiquement stockée dans le registre, associée à la clé "$this->_explicitType".
return $this->__getGoogleAjaxSearchApiKey();
}
private function __getGoogleAjaxSearchApiKey()
{
if (null === $this->_key)
{
$options = $this->getOptions();
$bootStart = $this->getBootstrap();
// On s'assure que les ressources utilisées ont été initialisées au préalable.
$bootStart->bootstrap('view');
$view = $bootStart->getResource('view');
...
$key = (isset($options['registry_key']) && !is_numeric($options['registry_key']))
? $options['registry_key']
: self::DEFAULT_REGISTRY_KEY;
Zend_Registry::set($key, $this->_key);
}
return $this->_key;
}
}
Initialisation de la ressource
L'initialisation de la ressource se fait dans le fichier d'initialisation de l'application.
Note: Le nom utilisé pour désigner la ressource est défini par la propriété "$_explicitType
" de la classe qui implémente la ressource (en l'occurrence "My_Resources_Googleajaxsearchapikey
").
pluginPaths.My_Resources = APPLICATION_PATH "/../library/My/Resources"
resources.googleAjaxSearchApiKey.value = "ABQIA..."
Accès à la ressource depuis le contrôleur d'action
Note: Le nom utilisé pour désigner la ressource est défini par la propriété "$_explicitType
" de la classe qui implémente la ressource (en l'occurrence "My_Resources_Googleajaxsearchapikey
").
public function loginAction()
{
$gkey = $this->getFrontController()->getParam('bootstrap')->getResource('googleAjaxSearchApiKey');
...
}
Accès à la ressource depuis une vue
Note: Le nom utilisé pour désigner la ressource est défini par la propriété "$_explicitType
" de la classe qui implémente la ressource (en l'occurrence "My_Resources_Googleajaxsearchapikey
").
$front = Zend_Controller_Front::getInstance();
$session = $front->getParam('bootstrap')->getResource('googleAjaxSearchApiKey');
La validateur (Ficher: "My/Validators/Login.php")
class My_Validators_Login extends Zend_Validate_Abstract
{
const NOT_MATCH = 'notMatch';
const MIN = 'notMin';
const MAX = 'notMax';
const FIRST = 'notFirst';
public $minimum = 6;
public $maximum = 12;
protected $_messageVariables = array
(
'min' => 'minimum',
'max' => 'maximum'
);
protected $_messageTemplates = array
(
self::NOT_MATCH => "Le login '%value%' que vous avez entré n'est pas valide. Seuls les caractères suivants sont autorisés : Les lettres non accentuées, les chiffres (de 0 à 9), le point (.), le souligné (_) et le tiret (-).",
self::MIN => "Le login '%value%' que vous avez entré n'est pas valide. La taille minimum autorisée est de '%min%' caractères.",
self::MAX => "Le login '%value%' que vous avez entré n'est pas valide. La taille maximale autorisée est de '%max%' caractères.",
self::FIRST => "Le login '%value%' que vous avez entré n'est pas valide. Un login doit commencer par une lettre non accentuée."
);
public function isValid($inValue)
{
$isValid = true;
$this->_setValue($inValue);
if (strlen($inValue) < $this->minimum)
{
$this->_error(self::MIN);
$isValid = false;
}
if (strlen($inValue) > $this->maximum)
{
$this->_error(self::MAX);
$isValid = false;
}
if (! preg_match('/^[a-z]/i', $inValue))
{
$this->_error(self::FIRST);
$isValid = false;
}
if (! preg_match('/^[a-z0-9_\.\-]+$/i', $inValue))
{
$this->_error(self::NOT_MATCH);
$isValid = false;
}
return $isValid;
}
}
Utilisation, dans la classe qui implémente le formulaire :
$login = new Zend_Form_Element_Text('login');
$login->setRequired(true)->addValidator('login');
Note: Cet exemple suppose que le chargeur automatique de classe est correctement configuré (
autoloaderNamespaces.0 = "My"
).
$valLoginNotEmpty = new Zend_Validate_NotEmpty();
$valLoginNotEmpty->setMessage("Vous n'avez pas spécifié de login.");
$numberRegExp = new Zend_Validate_Regex('/^0?\d{9}$/');
$numberRegExp->setMessage("Le numéro de téléphone que vous avez saisi n'est pas valide. Un numéro de téléphone se compose de 9 ou 10 chiffres (le zéro, en tête de numéro est facultatif).");
// Warning! Setting a translation may interfer with the validator' settings.
$login->setRequired(true)->addValidator($valLoginNotEmpty, true);
$numero->addValidator($numberRegExp, true);
/**
* Ce validateur vérifie qu'au moins un champ du formulaire est renseigné, sur une liste de champs.
* Doc: http://framework.zend.com/manual/en/zend.form.elements.html#zend.form.elements.validators
* @note CF notes sur: Validation Context
* setAllowEmpty(false)
* setRequired(true)
*
*/
class My_Form_Validator_OneAmongSeveral extends Zend_Validate_Abstract
{
const MISSING = 'oneAmongSeveralMissing';
protected $_messageTemplates = array();
private $__entries = null;
private static $__ok = false;
/**
* @param $inEntries Ce tableau contient la liste des noms des champs.
* @param $inMessage Message à renvoyer en cas d'erreur.
*/
public function __construct($inEntries, $inMessage)
{
$this->__entries = $inEntries;
$this->_messageTemplates[self::MISSING] = $inMessage;
}
/**
* The array "$context" contains all the formulars' fields.
*/
public function isValid($value, $context = null)
{
if (self::$__ok) { return true; }
$value = (string) $value;
$this->_setValue($value);
if (is_array($context))
{
foreach ($this->__entries as $entry)
{
if (isset($context[$entry]))
if (! empty($context[$entry]))
{ self::$__ok = true; return true; }
}
}
$this->_error(self::MISSING);
return false;
}
}
Utilisation :
// Validateurs pour les numéros de téléphone.
$numberRegExp = new Zend_Validate_Regex('/^(0?\d{9})?$/');
$numberRegExp->setMessage("Le numéro de téléphone que vous avez saisi n'est pas valide. Un numéro de téléphone se compose de 9 ou 10 chiffres (le zéro, en tête de numéro est facultatif).");
$oneAmongSeveral = new My_Form_Validator_OneAmongSeveral(array('callingNumber', 'calledNumber'), "Vous devez, au minimum, spécifier un numéro de téléphone : Le numéro de téléphone de l'appelant, ou celui de l'appelé.");
$callingNumber->setLabel('Numéro appellant')
->setDescription("Veuillez saisir le numéro de téléphone de l'appelant.")
->setAllowEmpty(false) // <= TRES IMPORTANT
->addValidator($oneAmongSeveral, true)
->addValidator($numberRegExp);
$calledNumber->setLabel('Numéro appelé')
->setDescription("Veuillez saisir le numéro de téléphone de l'appelé.")
->setAllowEmpty(false) // <= TRES IMPORTANT
->addValidator($oneAmongSeveral, true)
->addValidator($numberRegExp);
getRequest()->getModuleName(); ?>
getRequest()->getActionName(); ?>
getRequest()->getControllerKey(); ?>
getRequest()->getControllerName(); ?>
getRequest()->getParams(); ?>
escape(...); ?>
htmlList(...); ?>
getRequest()->getParams();
echo $this->action($action, $controller, null, $params);
$session = $front->getParam('bootstrap')->getResource('session');
?>
baseUrl('css/base.css'); ?>
headMeta()->...; ?>
headScript()->...; ?>
headStyle()->...; ?>
headTitle(...); ?>
// Clear all HTTP headers.
$this->getResponse()->clearAllHeaders(); // Optionnel.
// Disable the layout.
$this->_helper->layout->disableLayout();
// Disable the rendering of the current view.
$this->_helper->viewRenderer->setNoRender();
// Return JSON data.
$this->_helper->json($result);
// Rendering another view that the action's view.
$this->_helper->viewRenderer('the-new-view');
// Redirect with parameters
$redirector = $this->_helper->getHelper('Redirector');
$redirector->setGotoSimple($action, $controller, null, $inParams);
$redirector->redirectAndExit();
...
headLink(); ?>
headScript(); ?>
headTitle(); ?>
...
...
layout()->content; ?>
...
Implémentation
Ecriture de l'aide de vue (Fichier "application/views/standard/helpers/Tagger.php
")
class Application_Standard_View_Helper_Tagger
{
// Warning: The name of the function is "tagger"... like the name of the PHP file.
public function tagger() { return 'This is the "Application" view helper for the "Standard" terminal!'; }
}
Configuration de la vue
Dans la classe d'amorçage
$bootStart = $this->getBootstrap();
// Make sure that the "view" resource is bootstraped.
$bootStart->bootstrap('view');
// Get the "view" resource.
$view = $bootStart->getResource('view');
$view->addHelperPath(APPLICATION_PATH . "/views/standard/helpers", "Application_Standard_View_Helper");
Ou dans le fichier d'initialisation
resources.view.encoding = "UTF-8"
resources.view.helperPath.Application_Standard_View_Helper = APPLICATION_PATH . "/views/standard/helpers"
Utilisation
Depuis une vue
tagger(); >
Dans un décorateur
$formDecorator = new Zend_Form_Decorator_Form();
$formDecorator->setHelper('tagger');
Création dans la classe d'amorçage de l'application.
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initInlineJs()
{
$this->bootstrap('View');
$view = $this->getResource('View');
$view->placeholder('inlineJs')
// "prefix" -> markup to emit once before all items in collection
->setPrefix("\n\n");
}
}
Utilisation
Il est possible d'insérer de l'information dans le "place holder" depuis n'importe quel fichier PHP qui compose l'application.
class My_Forms_Decorators_SubmitLink extends Zend_Form_Decorator_Abstract
{
public function render($content)
{
...
$view = $element->getView();
$markup = sprintf($this->_format, $id, $label);
$js = "...";
// Le "placeholder" doit être initialisé dans le fichier "Bootstrap.php"!!!
$view->placeholder('inlineJs')->append($js);
return $markup;
}
}
Extraction du contenu (dans le fichier de gabarit, par exemple).
placeholder('inlineJs'); ?>
Implémentation
Fichier "My\Forms\Decorators\SubmitLink.php
".
class My_Forms_Decorators_SubmitLink extends Zend_Form_Decorator_Abstract
{
public function render($content)
{
$element = $this->getElement();
// Liste de toutes les méthodes disponibles:
// http://framework.zend.com/apidoc/core/Zend_Form/Element/Zend_Form_Element.html#getAttrib
$label = htmlentities($element->getLabel()); // Le texte du lien.
$id = htmlentities($element->getId()); // l'ID du compasant HTML.
$class = htmlentities($element->getAttrib('class')); // Le classe CSS.
$formId = htmlentities($element->getAttrib('formId')); // l'ID du formulaire associé.
$view = $element->getView();
// You create the HTML for this element.
$markup = ...
return $markup;
}
}
Utilisation
Le code ci-dessous assigne le décorateur à un élément du formulaire.
$decorator = new My_Forms_Decorators_SubmitLink();
$submit = new Zend_Form_Element($submitId);
$submit->setLabel('Editer le template')
->setAttrib('class', 'myClass')
->setAttrib('formId', $formularId)
->setDecorators(array($decorator));
Remarque: Il ne faut pas oublier d'initialiser le chargeur de classe ("autoloaderNamespaces.0 = "My"
").
L'adaptateur
Fichier "application/models/UsersMapper.php
".
class Application_Model_UsersMapper
{
private $__dbTable = null;
public function __construct()
{
$this->__dbTable = new Application_Model_DbTable_Users();
if (! $this->__dbTable instanceof Zend_Db_Table_Abstract) { throw new Exception('Invalid table data gateway provided'); }
}
public function getDbTable() { return $this->__dbTable; }
}
La table
Fichier "application/models/DbTable/Users.php
"
class Application_Model_DbTable_Users extends Zend_Db_Table_Abstract
{
/** Nom de la table. */
protected $_name = 'users';
/** Nom du champ qui représente la clé primaire de la table. */
protected $_primary = 'idusers';
/**
* Cette variable contient la liste des tables qui "dépendent" de la présente table (de référence).
* Une table "dépendante" présente une clé étrangère vers une table de "référence".
*/
protected $_dependentTables = array ('profiles', 'participants', 'messages');
}
Accès à la base de donnée
class UserController extends Zend_Controller_Action
{
...
private function __getUserMapper()
{
$db = new Application_Model_UsersMapper();
$this->__userMapper = $db;
return $this->__userMapper;
}
...
}
Initialisation de l'application
Dans le fichier d'initialisation de l'application :
app.db.base1.adapter = "pdo_mysql"
app.db.base1.params.host = "hostname1"
app.db.base1.params.username = "user1"
app.db.base1.params.password = "pass1"
app.db.base1.params.dbname = "dbname1"
app.db.base1.isDefaultTableAdapter = false
app.db.base2.adapter = "pdo_mysql"
app.db.base2.params.host = "hostname2"
app.db.base2.params.username = "user2"
app.db.base2.params.password = "pass1"
app.db.base2.params.dbname = "dbname2"
app.db.base2.isDefaultTableAdapter = false
Dans la classe d'amorçage de l'application, on crée les adaptateurs vers les bases de données.
protected function _initApp()
{
// On stocke la configuration globale.
$config = $this->getOption('app');
Zend_Registry::set('app', $config);
// On crée les adaptateurs vers les bases de données.
$databases = array();
foreach ($config['db'] as $dbName => $dbConfig)
{
$databases[$dbName] = Zend_Db::factory($dbConfig['adapter'], $dbConfig['params']);
if ($dbConfig['isDefaultTableAdapter']) { Zend_Db_Table_Abstract::setDefaultAdapter($databases[$dbName]); }
}
Zend_Registry::set('databases', $databases);
return $config;
}
Création des tables
self::$_table11 = new Application_Model_Base1_DbTable_Table1(array('db' => self::_getAdapter('base1')));
self::$_table21 = new Application_Model_Base2_DbTable_Table1(array('db' => self::_getAdapter('base2')));
...
protected static function _getAdapter($inDbName)
{
$adapters = Zend_Registry::get('databases');
return $adapters[$inDbName];
}
Où placer les définitions des modèles de données?
Hypothèse : Vous utilisez la configuration par défaut pour les ressources liées à l'application.
// --- Base 1 ---
// class Application_Model_Base1_MyMapper
application/models/Base1/MyMapper.php
// class Application_Model_Base1_DbTable_Table1
application/models/Base1/DbTable/Table1.php
// --- Base 2 ---
// class Application_Model_Base2_MyMapper
application/models/Base2/MyMapper.php
// class Application_Model_Base2_DbTable_Table1
application/models/Base2/DbTable/Table1.php
Vous pouvez changer l'emplacement des fichiers qui définissent les modèles de base :
// See section about the resource auto loader.
$resourceLoader = new Zend_Loader_Autoloader_Resource(...);
$resourceLoader->addResourceType(..., ..., ...);
public function addUser($inRecord, &$outError)
{
try
{
$this->__userTable->insert($inRecord);
}
catch (Exception $e)
{
if (is_a($e->getPrevious(), 'PDOException'))
{
// WARNING !!!
// The values below depend on the adapter. The following code assumes that we are using MySql.
// The values below may change depending on the version of MySql.
$mySqlErrorCode = $e->getPrevious()->errorInfo[1];
$mySqlErrorMessage = $e->getPrevious()->errorInfo[2];
if ($mySqlErrorCode == 1062)
if (preg_match('/for\s+key\s+\'pseudoidx\'/', $mySqlErrorMessage))
{ $outError = 'pseudo'; return false; }
}
throw (new Exception("Une erreur interne est survenue. L'administrateur du site a été averti."));
}
return true;
}
Le point important est le suivant :
if (is_a($e->getPrevious(), 'PDOException'))
...
$mySqlErrorCode = $e->getPrevious()->errorInfo[1]; // The numerical code
$mySqlErrorMessage = $e->getPrevious()->errorInfo[2]; // The textual error message
L'ID de l'élément qui représente le bouton d'envoi est le texte passé au constructeur de l'élément ($submit = new Zend_Form_Element_Submit('submit ');
).
Pour définir le texte qui apparaît sur le bouton, il faut utiliser la méthode "setLabel()
" ($submit->setLabel('Envoyer');
).
On personnalise le texte qui va apparaître sur l'image (Fichier "My\Captchas\Image.php
").
require_once 'Zend/Captcha/Image.php';
// See: http://zendframework.com/issues/browse/ZF-9557
class My_Captchas_Image extends Zend_Captcha_Image
{
// public function __construct() { parent::__construct(); }
public function getUseNumbers()
{
return $this->_useNumbers;
}
public function setUseNumbers($useNumbers)
{
$this->_useNumbers = $useNumbers;
return $this;
}
protected function _generateWord()
{
// WARNING: Do **NOT** use uppercase letters !!!!
$word = parent::_generateWord();
$word = preg_replace(array('/i/', '/j/', '/x/', '/q/', '/r/'), array('l', 'l', 'h', 'p', 'o'), $word);
$this->_setWord($word);
return $word;
}
}
Puis on crée le filtre anti-robot.
const CAPTCHA_NAME = 'captchaimg';
...
// Specific sesson is very important!
$captchaSession = new Zend_Session_Namespace('My_Captcha_'. self::CAPTCHA_NAME);
$captchaOpt = Zend_Registry::get('captcha');
$captchaimg = New My_Captchas_Image(self::CAPTCHA_NAME);
$captchaimg->setFont(WinCompat_Path($captchaOpt['fontPath']));
$captchaimg->setImgDir(WinCompat_Path($captchaOpt['imageDir']));
$captchaimg->setImgUrl($captchaOpt['imageUrl']);
$captchaimg->setTimeout($captchaOpt['timeout']);
$captchaimg->setHeight($captchaOpt['height']);
$captchaimg->setWidth($captchaOpt['width']);
$captchaimg->setWordLen($captchaOpt['wordLen']);
$captchaimg->setDotNoiseLevel($captchaOpt['dotNoiseLevel']);
$captchaimg->setLineNoiseLevel($captchaOpt['lineNoiseLevel']);
$captchaimg->setUseNumbers($captchaOpt['useNumbers']);
$captchaimg->setSession($captchaSession);
$captcha = new Zend_Form_Element_Captcha(self::CAPTCHA_NAME, array('captcha' => $captchaimg));
Utilisation simple
Dans le fichier d'initialisation de l'application :
resources.log.stream.writerName = "Stream"
resources.log.stream.writerParams.stream = APPLICATION_PATH "/../logs/application.log"
resources.log.stream.writerParams.mode = "a"
Dans le code de l'application :
$logger = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('log');
$logger->log("Search sorties ($condition)", Zend_Log::INFO);
Utilisation avancée
Dans le fichier d'initialisation de l'application :
log.path = APPLICATION_PATH "/../logs/application.log"
Dans la classe d'amorçage de l'application :
protected function _initLogger()
{
$id = uniqid();
$config = $this->getOption('log');
$format = '%timestamp% %priorityName% (%priority%) [' . $id . ']: %message%' . PHP_EOL;
$redacter = new Zend_Log_Writer_Stream($config['path']);
$formater = new Zend_Log_Formatter_Simple($format);
$redacter->setFormatter($formater);
$logger = new Zend_Log();
$logger->addWriter($redacter);
Zend_Registry::set('logger', $logger);
$logger->log("Start session \"$id\".", Zend_Log::DEBUG);
return $logger;
}
class IndexController extends Zend_Controller_Action
{
// Référence: http://framework.zend.com/manual/fr/zend.controller.actionhelpers.html
private $__params = null;
public function init()
{
$this->__params = $this->getRequest()->getParams();
}
public function indexAction()
{
$this->view->params = $this->__params;
}
public function destinationAction()
{
$this->view->params = $this->__params;
}
public function redirectAction()
{
$test = 1;
$redirector = $this->_helper->getHelper('Redirector');
// Le code ci-dessous provoque une redirection vers l'action "destination".
// Les paramètres sont transmis.
if (0 === $test)
{
$redirector->setCode(303)
->setGotoSimple('destination', 'index', null, $this->__params);
$redirector->redirectAndExit();
}
// Le code ci-dessous provoque une redirection vers l'action "destination".
// La méthode setGotoUrl() ne permet pas de spécifier des paramètres.
// Des paramètres peuvent, éventuellement être concaténés à l'URL de destination (manuellement).
if (1 === $test)
{
$redirector->setCode(303)
->setGotoUrl('/index/destination');
$redirector->redirectAndExit();
}
}
}
Les vues partielles permettent de définir des éléments de vues complexes (ex: des menus de navigation). Ces vues peuvent être insérées dans d'autres vues.
Créer un fichier PHTML qui contient la vur partielle. Exemple "application/views/partials/MenuActions.phtml
".
Dans une vue (qui peut être un gabarit) :
partial('/partials/MenuActions.phtml'); ?>
Remarque: Il est possible de passer les valeurs aux vues partielles (http://framework.zend.com/manual/fr/zend.view.helpers.html )
Retourner du JSON depuis une action :
$this->_helper->json($result);
Retourner du texte depuis une action :
$this->getResponse()->clearAllHeaders(); // Optionnel.
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
echo "my text here";
// On définit le répertoire d'installation de l'application.
define('APPLICATION_PATH', realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'application'));
// On définit le chemin vers le fichier de configuration.
define('CONFIGURATION_PATH', APPLICATION_PATH . DIRECTORY_SEPARATOR . 'configs' . DIRECTORY_SEPARATOR . 'application.ini');
// On définit l'environnement de l'application.
define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'development'));
// On définit les chemins de chargement des librairies PHP.
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'library'),
get_include_path(),
)));
// On configure le système d'auto-chargement des classes.
require_once 'Zend' . DIRECTORY_SEPARATOR . 'Loader' . DIRECTORY_SEPARATOR . 'Autoloader.php';
// Déclaration de l'espace de nom **des classes** pour le framework ZEND.
Zend_Loader_Autoloader::getInstance()->registerNamespace('Zend_');
// Déclaration de l'espace de nom **des ressources** applicatives.
// On déclare le chemin vers une ressource, désignée par son espace de nom.
// En l'occurrence : La ressource, désignée par l'espace de nom "Application", est stockée dans le répertoire "APPLICATION_PATH".
// NOTE: Les autoloaders de ressources s'enregistrent dans l'autoloader à leur instanciation.
// http://framework.zend.com/manual/fr/zend.loader.autoloader-resource.html
// NOTE: Une classe qui commence par "Application_" sera considérée comme une "ressource applicative".
$resourceLoader = new Zend_Loader_Autoloader_Resource( array( 'namespace' => 'Application',
'basePath' => APPLICATION_PATH));
// On déclare un "type" de "ressource applicative" (le type "model", en l'occurence).
// En l'occurrence, les ressources de type "model", sont stockées dans le sous-répertoire "model/" du répertoire des "ressources applicatives", soit:
// APPLICATION_PATH . DIRECTORY_SEPARATOR . 'models'.
// Et les noms des classes qui implémentent ce type de "ressource applicative" commencent par : "Application_Model"
$resourceLoader->addResourceType('model', 'models/', 'Model');
// On charge la configuration.
$config = new Zend_Config_Ini(CONFIGURATION_PATH, APPLICATION_ENV);
Zend_Registry::set('config', $config);
// ------------------------------------------------------------------------
// Procédure spécifique à l'application.
// ------------------------------------------------------------------------
// Déclaration d'un espace de nom pour les classes.
Zend_Loader_Autoloader::getInstance()->registerNamespace('My_');
Zend_Loader_Autoloader::getInstance()->registerNamespace('Libs_');
// Connection à la base de données.
$databaseConfig = $config->app->db->dbcdr;
$databaseHandler = Zend_Db::factory($databaseConfig->adapter, $databaseConfig->params);
if ($databaseConfig->isDefaultTableAdapter) { Zend_Db_Table_Abstract::setDefaultAdapter($databaseHandler); }
Zend_Registry::set('database', $databaseHandler);
$povHd = new Application_Model_Base1_PovMapper();
...
-a -t
// Configuration des serveurs de mail.
// Vous pouvez ajouter d'autres serveurs d'email (Yahoo,...).
// Pour chaque serveur ajouté, vous pouvez ajouter des comptes.
$smtpConfigs = array('gmail.com' => array('address' => 'smtp.gmail.com',
'auth' => 'login',
'ssl' => 'tls',
'port' => 587,
'myAccount' => array('username' => 'myAccount@gmail.com',
'password' => 'MyPassword',
'identity' => 'MyIdentity')
)
);
// Configuration de l'environnement PHP.
ini_set('include_path', ini_get('include_path') . ':/path/to/the/Zend/library');
// Initialisation du chargeur de classe.
require_once 'Zend' . DIRECTORY_SEPARATOR . 'Loader' . DIRECTORY_SEPARATOR . 'Autoloader.php';
Zend_Loader_Autoloader::getInstance();
// Analyse de la ligne de commande.
$options = getopt('s:a:t:');
if (FALSE === $options) { echo "ERROR: Invalid options.\n"; exit(1); }
if (! array_key_exists('s', $options)) { echo "ERROR: Mandatory option -s is missing!\n"; exit(1); }
if (! array_key_exists('a', $options)) { echo "ERROR: Mandatory option -a is missing!\n"; exit(1); }
if (! array_key_exists('t', $options)) { echo "ERROR: Mandatory option -t is missing!\n"; exit(1); }
$server = $options['s'];
$account = $options['a'];
$destination = $options['t'];
// Test de cohérence.
if (! array_key_exists($server, $smtpConfigs)) { echo "ERROR: Server \"$server\" does not exist!\n"; exit(1); }
if (! array_key_exists($account, $smtpConfigs[$server])) { echo "ERROR: Account \"$account\" does not exist, for server \"$server\"!\n"; exit(1); }
// Création de l'adaptateur de transport.
$transport = new Zend_Mail_Transport_Smtp($smtpConfigs[$server]['address'], array('auth' => $smtpConfigs[$server]['auth'],
'ssl' => $smtpConfigs[$server]['ssl'],
'port' => $smtpConfigs[$server]['port'],
'username' => $smtpConfigs[$server][$account]['username'],
'password' => $smtpConfigs[$server][$account]['password']));
if (is_null($transport)) { echo "ERROR: Could not create transport adapter!\n"; exit(1); }
// Création su mail.
$mail = new Zend_Mail();
$mail->setBodyText("Ceci est le texte de l'email.");
$mail->setFrom($smtpConfigs[$server][$account]['username'], $smtpConfigs[$server][$account]['identity']);
$mail->addTo($destination);
$mail->setSubject('Sujet de test');
// Envoi du mail.
try { $mail->send($transport); }
catch (Exception $e) { echo "ERROR: " . urlencode($e->getMessage()) . "\n"; exit(1); }
?>
Le serveur
'ok', 'message' => '');
}
public function CANCEL($arg)
{
logger('CANCEL(' . dumpArgs($arg) . ")\n");
logger('idCommand = ' . $arg->idCommand . "\n");
return array( 'status' => 'ok', 'message' => '');
}
public function STATUS($arg)
{
logger('STATUS(' . dumpArgs($arg) . ")\n");
return array( 'status' => 'ok', 'message' => '');
}
public function ROLLBACK($arg)
{
logger('ROLLBACK(' . dumpArgs($arg) . ")\n");
logger('idCommand = ' . $arg->idCommand . "\n");
logger('idCommand_rollbaxk = ' . $arg->idCommand_rollback . "\n");
return array( 'status' => 'ok', 'message' => '');
}
}
// On crée un serveur dont l'interface est décrite par le WSDL.
$serveur = new SoapServer('C:\wamp\www\porta.wsdl');
// On injecte dans le serveur le code pour implémenter les opérations.
$serveur->setClass('serverClass');
// On lance le serveur.
$serveur->handle();
// Cette fonction écrit des messages dans un fichier.
function logger($message)
{
$fd = fopen('C:\wamp\www\log', 'a+');
if ($fd == FALSE) { return; }
fwrite($fd, "$message");
fclose($d);
}
// Cette fonction liste les attributs d'un objet.
function dumpArgs($arg)
{
$text = '';
if (is_object($arg))
{
$properties = get_object_vars($arg);
foreach ($properties as $key => $value) { $text .= "$key => $value" . ", "; }
}
$text = preg_replace('/, $/', '', $text);
return $text;
}
?>
Le client
Methods";
$functions = $client->__getFunctions();
foreach ($functions as $f) { echo "$f "; }
// On exécute une opération, présente dans la liste des opérations définies dans le WSDL.
try
{
$res = $client->CANCEL(array('idCommand' => '10'));
vardump($res);
$res = $client->ROLLBACK(array('idCommand' => '10', 'idCommand_rollback' => 11));
vardump($res);
$res = $client->STATUS(array('idCommand' => '10'));
vardump($res);
}
catch (Exception $e)
{
echo '';
printf("Message = %s\n",$e->__toString());
echo ' ';
echo $client->__getLastResponse();
}
echo ' ';
echo 'OK';
?>
Création du WSDL
ATTENTION !!!
En cas de problème, pensez à régénérer les "associations de contenu" ("Generate Binding Content") - comme indiquer sur le figure ci-dessous.
Dans la description WSDL ci-dessous, il faut modifier la ligne ci-dessous (à la fin du WSDL).
Ce document décrit l'interface de configuration du système VOIP pour la demande de portage de numéros de téléphones.
Le numéro à configurer dans le TS.
Le type de portage demandé. Il s'agit d'une
chaîne de caractères qui permet de nommer le
type de portage. Ce nom est stocké à titre
informatif.
La date prévue à laquelle cette commande sera injectée dans la configuration du TS.
Code opérateur du l'atributaire. Ex: ALTD pour ALTITUDE TELECOM.
Code opérateur du donneur. Ex: ALTD pour ALTITUDE TELECOM.
Code opérateur du receveur. Ex: ALTD pour ALTITUDE TELECOM.
Pour les demandes de portage sortant (uniquement), ce paramètre représente le préfix associé à l'opérateur receveur. Pour les demandes de portages entrants, ce paramètre n'a pas de sens.
Préfixe associé à l'opérateur donneur.
Préfixe associé à l'opérateur receveur.
Il s'agit d'une chaîne de caractères (limitée à 45 caractères) qui identifie, de façon unique, la demande.
Cette valeur représente le status de l'opération.
Si une erreur se produit, ce paramètre contient un message qui décrit la nature de l'erreur.
Il s'agit de la chaîne de caractères préalablement passée en paramètre du service WEB.
Il s'agit d'une chaîne de caractères (limitée à 45 caractères) qui identifie, de façon unique, "cette" demande de ROLLBACK.
Il s'agit d'une chaîne de caractères (limitée à 45 caractères) qui identifie, de façon unique, "cette" demande de ROLLBACK.
Il s'agit de l'identificateur (idCommand) de la commande pour laquelle on désire effectuer un retour arrière.
Cette valeur représente le status de l'opération.
Si une erreur se produit, ce paramètre contient un message qui décrit la nature de l'erreur.
Il s'agit de la chaîne de caractères préalablement passée en paramètre du service WEB.
Il s'agit de la date à laquelle sera planifiée l'opération de retour arrière.
Il s'agit d'une chaîne de caractères (limitée à 45 caractères) qui identifie, de façon unique, la demande.
Cette valeur représente le status de l'opération identifiée par son numéro d'enregistrement.
Cette valeur représente le status de l'opération
identifiée par son numéro d'enregistrement.
Si une erreur se produit, ce paramètre contient
un message qui décrit la nature de l'erreur.
Il s'agit de la chaîne de caractères préalablement passée en paramètre du service WEB.
Il s'agit d'une chaîne de caractères (limitée à 45 caractères) qui identifie, de façon unique, la demande de portage à annuler.
Cette valeur représente le status de l'opération.
Cette valeur représente le status de
l'opération.
Si une erreur se produit, ce paramètre contient un message qui décrit la nature de l'erreur.
Il s'agit de la chaîne de caractères préalablement passée en paramètre du service WEB.
Ce paramètre représente le numéro de téléphone sur lequel
est effectuée l'opération.
Cette méthode permet d'effectuer une opération de portage sur un numéro de téléphone.
Cette méthode permet d'annuler une opération de portage, dans les deux heures qui suivent sa date d'échéance.
Cette méthode permet de connaître l'état d'une opération de portage.
Cette méthode permet d'annuler une opération de portage, avant sa date d'échéance. C'est-à -dire, avant la date prévue pour son injection dans le système VOIP.