Tutorials
PHP 6: hoe en wat?
Wat kunnen we van PHP 6 gaan verwachten? De grote schoonmaak is begonnen! Weg met magic_quotes, register_globals, safe_mode en mysql_query()! En welkom namespaces en label/goto. Begin nu al je scripts veilig en logisch te maken!
Pagina 1
Installatie PHP 6 naast PHP 5
(voor WAMP kun je ook een add-on downloaden: http://www.wampserver.com/en/add-ons.php. zie reactie van Blanche bij de reacties op deze tutorial)
1. Download PHP 6 van http://snaps.php.net/.
2. Unzip de inhoud van de zip-file naar een gewenste folder. Ik unzip 'm bij dit voorbeeld naar C:/server/php6.
3. Maak daarna een gewenste document root voor je PHP 6 bestanden, we gaan het namelijk in een heel andere map gooien dan PHP 5. Ik heb zelf mijn PHP 5 draaien in C:/server/www, en ik gooi mijn PHP 6 in C:/server/www6.
4. Vervolgens ga je naar je httpd.conf-bestand van Apache (te vinden in Apache2/conf). Zet hier ergens de volgende tekst in:
Je moet in bovenstaande code alleen de DocumentRoot veranderen naar jouw document root voor PHP 6, en ScriptAlias naar de map waar je PHP 6 hebt geunzipt. Je kunt eventueel ook de poort 83 veranderen naar een andere poort.
5. Herstart Apache!
Je bent nu klaar met de installatie. Je PHP 6 is te bereiken op http://localhost:83.
1. Download PHP 6 van http://snaps.php.net/.
2. Unzip de inhoud van de zip-file naar een gewenste folder. Ik unzip 'm bij dit voorbeeld naar C:/server/php6.
3. Maak daarna een gewenste document root voor je PHP 6 bestanden, we gaan het namelijk in een heel andere map gooien dan PHP 5. Ik heb zelf mijn PHP 5 draaien in C:/server/www, en ik gooi mijn PHP 6 in C:/server/www6.
4. Vervolgens ga je naar je httpd.conf-bestand van Apache (te vinden in Apache2/conf). Zet hier ergens de volgende tekst in:
Listen 83
<Virtualhost _default_:83>
ServerName localhost
ServerAdmin me@localhost
DirectoryIndex index.html index.php
ErrorLog logs/error.log
# http://httpd.apache.org/docs-2.1/mod/core.html.en#limit
<Location />
<Limit CONNECT>
Order allow,deny
Allow from all
</Limit>
</Location>
DocumentRoot "C:/server/www6/"
ScriptAlias /cgi-bin/ "C:/server/php6/"
Action php6-script /cgi-bin/php-cgi.exe
AddHandler php6-script .php .html
</Virtualhost>
Je moet in bovenstaande code alleen de DocumentRoot veranderen naar jouw document root voor PHP 6, en ScriptAlias naar de map waar je PHP 6 hebt geunzipt. Je kunt eventueel ook de poort 83 veranderen naar een andere poort.
5. Herstart Apache!
Je bent nu klaar met de installatie. Je PHP 6 is te bereiken op http://localhost:83.
Pagina 2
Inleiding
Al een vrij lange tijd zijn er bèta's te verkrijgen van PHP 6. In PHP 6 zullen er een aantal dingen veranderen ten opzichte van PHP 5. Eén belangrijke wijziging zal echter al bij de upgrade naar PHP 5.3 plaatsvinden binnenkort, namelijk de namespaces.
PHP 6 is nog niet stabiel en bruikbaar, maar het is alvast leerzaam om te gebruiken en via deze tutorial leer je o.a. hoe je heel snel PHP 6 naast PHP 5 installeert en draait op je lokale server zonder te hoeven knoeien met .php6-extensies.
Er wordt al lang aan PHP 6 gewerkt en PHP 5 is al 3,5 jaar de standaard, dus er zullen waarschijnlijk geen grote wijzigingen meer bij komen. Lees alles zorgvuldig door, want PHP 6 eist dat je netjes programmeert volgens de nieuwere standaarden. Ik denk dat dit voor veel mensen problemen kan opleveren, vooral met magic quotes en de mysql_*() functies.
Het is heel lastig informatie te vinden op internet over PHP 6 en ik heb zelfs Duitse sites bekeken, maar ik heb zoveel mogelijk nuttige en recente informatie bij elkaar proberen te krijgen en alles zelf getest. Er komen natuurlijk nog meer wijzigingen dan de punten in deze tutorial, maar hier komen de belangrijksten!
Let er op dat alles wat in deze tutorial staat nog veranderd kan worden door het PHP team, al is het niet heel waarschijnlijk, want er is al veel veranderd en het lijkt naar een eindstadium toe te gaan.
PHP WORDT EINDELIJK VOLWASSEN!!!
Zie ook http://wiki.pooteeweet.org/PhP60 voor een complete lijst.
PHP 6 is nog niet stabiel en bruikbaar, maar het is alvast leerzaam om te gebruiken en via deze tutorial leer je o.a. hoe je heel snel PHP 6 naast PHP 5 installeert en draait op je lokale server zonder te hoeven knoeien met .php6-extensies.
Er wordt al lang aan PHP 6 gewerkt en PHP 5 is al 3,5 jaar de standaard, dus er zullen waarschijnlijk geen grote wijzigingen meer bij komen. Lees alles zorgvuldig door, want PHP 6 eist dat je netjes programmeert volgens de nieuwere standaarden. Ik denk dat dit voor veel mensen problemen kan opleveren, vooral met magic quotes en de mysql_*() functies.
Het is heel lastig informatie te vinden op internet over PHP 6 en ik heb zelfs Duitse sites bekeken, maar ik heb zoveel mogelijk nuttige en recente informatie bij elkaar proberen te krijgen en alles zelf getest. Er komen natuurlijk nog meer wijzigingen dan de punten in deze tutorial, maar hier komen de belangrijksten!
Let er op dat alles wat in deze tutorial staat nog veranderd kan worden door het PHP team, al is het niet heel waarschijnlijk, want er is al veel veranderd en het lijkt naar een eindstadium toe te gaan.
PHP WORDT EINDELIJK VOLWASSEN!!!
Zie ook http://wiki.pooteeweet.org/PhP60 voor een complete lijst.
Pagina 3
Bye@ Register_globals, magic_quotes, mysql_query()
Register globals
Register_globals zullen in PHP 6 niet meer bestaan, ook niet optioneel in te stellen. Dus je zult altijd superglobals moeten gebruiken (zie mijn tutorial over superglobals). Ook de HTTP_GET_VARS- en HTTP_POST_VARS-varianten vervallen.
Eureka! Dit vergroot de veiligheid met een grote factor, omdat brakke PHP scripts gewoon niet meer gerund kunnen worden.
BELANGRIJK: MAGIC QUOTES
Ook magic quotes zullen volledig vervallen. Voor de meeste programmeurs op PHPhulp zal dit een probleem vormen. Men is gewend dat waarden uit $_GET en $_POST automatisch geëscaped zijn. Dit zal niet meer het geval zijn. Voor professionals zoals ik is dit een grote opluchting. Ook bevordert dit de veiligheid, omdat iedereen er nu actief op moet gaan letten.
Voorbeeld, DIT KAN STRAKS ECHT NIET MEER:
<?php
$sql = "
SELECT
*
FROM
tabel
WHERE
veld = '" . $_POST['naam'] . "'
";
$query = mysql_query($sql);
?>
De waarde van $_POST['naam'] zal namelijk niet meer geëscaped worden, dus is SQL injection zo mogelijk. Je moet dit voortaan zelf beveiligen. Vroeger zou bijvoorbeeld de string pizza's worden omgezet naar pizza\'s. NU NIET MEER. Nu levert het een dikke error op omdat pizza's je query verpest vanwege het aanhalingsteken.
mysql_query() waarschijnlijk weg
Dat beveiligen doe je met PDO (tutorial). De gewone database-functies als mysql_query() zijn waarschijnlijk namelijk ook standaard uit PHP 6 gesloopt maar zijn via extensies nog te krijgen (hier moet nog een definitieve beslissing over gemaakt worden). PDO blijft de enige standaard voor verschillende databases. Met PDO kun je o.a. prepared statements gebruiken en zo netjes je scripts beveiligen. Hier staan genoeg tutorials over op PHPhulp.
<?php
mysql_query();
?>
Hetzelfde geldt voor mysqli_*, maar alles onder voorbehoud.
Safe mode
Ook safe mode zal niet meer beschikbaar zijn. Niet echt interessant voor ons programmeurs denk ik.
Register_globals zullen in PHP 6 niet meer bestaan, ook niet optioneel in te stellen. Dus je zult altijd superglobals moeten gebruiken (zie mijn tutorial over superglobals). Ook de HTTP_GET_VARS- en HTTP_POST_VARS-varianten vervallen.
Eureka! Dit vergroot de veiligheid met een grote factor, omdat brakke PHP scripts gewoon niet meer gerund kunnen worden.
BELANGRIJK: MAGIC QUOTES
Ook magic quotes zullen volledig vervallen. Voor de meeste programmeurs op PHPhulp zal dit een probleem vormen. Men is gewend dat waarden uit $_GET en $_POST automatisch geëscaped zijn. Dit zal niet meer het geval zijn. Voor professionals zoals ik is dit een grote opluchting. Ook bevordert dit de veiligheid, omdat iedereen er nu actief op moet gaan letten.
Voorbeeld, DIT KAN STRAKS ECHT NIET MEER:
<?php
$sql = "
SELECT
*
FROM
tabel
WHERE
veld = '" . $_POST['naam'] . "'
";
$query = mysql_query($sql);
?>
De waarde van $_POST['naam'] zal namelijk niet meer geëscaped worden, dus is SQL injection zo mogelijk. Je moet dit voortaan zelf beveiligen. Vroeger zou bijvoorbeeld de string pizza's worden omgezet naar pizza\'s. NU NIET MEER. Nu levert het een dikke error op omdat pizza's je query verpest vanwege het aanhalingsteken.
mysql_query() waarschijnlijk weg
Dat beveiligen doe je met PDO (tutorial). De gewone database-functies als mysql_query() zijn waarschijnlijk namelijk ook standaard uit PHP 6 gesloopt maar zijn via extensies nog te krijgen (hier moet nog een definitieve beslissing over gemaakt worden). PDO blijft de enige standaard voor verschillende databases. Met PDO kun je o.a. prepared statements gebruiken en zo netjes je scripts beveiligen. Hier staan genoeg tutorials over op PHPhulp.
<?php
mysql_query();
?>
Fatal error: Call to undefined function mysql_query() in C:\server\www6\mysql.php on line 2Hetzelfde geldt voor mysqli_*, maar alles onder voorbehoud.
Safe mode
Ook safe mode zal niet meer beschikbaar zijn. Niet echt interessant voor ons programmeurs denk ik.
Pagina 4
Namespaces (vanaf PHP 5.3)
Een grote toevoeging aan PHP 6 zijn de namespaces. Op dit moment heb je namelijk vaak het probleem dat 2 verschillende frameworks of pakketten dezelfde class-namen hebben. Bijvoorbeeld allebei Date. Dan krijg je een dikke error en dat is een probleem. Daarnaast heb je nu vaak lange class-namen, kijk maar naar Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive in het Zend Framework. Java lost dit op met packages en C++ met namespaces. Je kunt een class laten vallen onder een bepaalde namespace en zal dan die namespace moeten "importeren" (gebruiken) om de class elders aan te roepen.
Voorbeeld:
include.php:<?php
namespace foonamespace;
class Foobar
{
public $foobar;
}
?>
uitvoeren.php:<?php
require 'include.php';
$inst = new Foobar;
$inst->foobar = 'foobar';
echo $inst->foobar;
?>
Resultaat:
De reden van deze error is dat PHP nu alleen kijkt binnen de huidige namespace (default namespace want ik heb in uitvoeren.php geen namespace aangegeven). Als ik echter dit doe, dan werkt het wel:
nieuwe uitvoeren.php:<?php
namespace foonamespace;
require 'include.php';
$inst = new Foobar;
$inst->foobar = 'foobar';
echo $inst->foobar;
// output: foobar
?>
Deze manier is alleen niet helemaal de bedoeling. Je moet classes die in hetzelfde pakket vallen onder dezelfde namespace gooien. Als je dan zo'n class elders wil gebruiken doe je het gewoon zo:
uitvoeren.php:<?php
require 'include.php';
$inst = new foonamespace::Foobar;
$inst->foobar = 'foobar';
echo $inst->foobar;
?>
Let op de foonamespace::. Daarmee geef ik aan dat de class Foobar zich begeeft in de namespace foonamespace (zoals aangegeven in include.php).
Je kunt zo ook meerdere lagen diep gaan:
<?php
namespace PHPhulp::Database;
class SQLstatements
{
// iets;
}
?>
Deze class kun je dan weer later aanroepen met PHPhulp::Database::SQLstatements.
Namen korter maken
PHPhulp::Database::SQLstatements is nog steeds vrij lang, dus dat moet korter! Daar heb je de use-operator voor gekregen:
include.php:<?php
namespace PHPhulp::Database;
class SQLstatements
{
// iets;
}
?>
uitvoeren.php:<?php
use PHPhulp::Database as PHP_DB;
$inst = new PHP_DB::SQLstatements();
// etc
?>
Het PHP team heeft de operator use in eerste instantie import genoemd, maar later toch herzien naar use. Dit komt omdat import al veel wordt gebruikt als functienaam in bestaande scripts, en het nu een reserved keyword moet worden. Dat zou betekenen dat veel bestaande scripts errors gaan opleveren. En het PHP team heeft use al reserved gemaakt vanaf PHP 5 door een foutje, waardoor de upgrade van PHP 5 naar PHP 6 geen problemen kan opleveren met de use-operator (hij is namelijk al reserved).
Let op: namespace-declaraties moeten helemaal bovenaan in het document.
Bedoeling
De bedoeling is dus dat je classes binnen hetzelfde pakket in dezelfde namespace zet. Bij een framework kun je subonderdelen in subnamespaces zetten met Hoofd::Sub en die aanroepen met Hoofd::Sub::Class (of een afkorting met use). Zo creëer je packages en heb je geen botsingen meer met class-namen en je hebt geen lange class-namen meer, je hoeft ze namelijk slechts bovenaan je document te importeren en verder kun je de afkorting gebruiken. Problemen opgelost.
Voorbeeld:
include.php:<?php
namespace foonamespace;
class Foobar
{
public $foobar;
}
?>
uitvoeren.php:<?php
require 'include.php';
$inst = new Foobar;
$inst->foobar = 'foobar';
echo $inst->foobar;
?>
Resultaat:
Fatal error: Class 'Foobar' not found in C:\server\www6\uitvoeren.php on line 4De reden van deze error is dat PHP nu alleen kijkt binnen de huidige namespace (default namespace want ik heb in uitvoeren.php geen namespace aangegeven). Als ik echter dit doe, dan werkt het wel:
nieuwe uitvoeren.php:<?php
namespace foonamespace;
require 'include.php';
$inst = new Foobar;
$inst->foobar = 'foobar';
echo $inst->foobar;
// output: foobar
?>
Deze manier is alleen niet helemaal de bedoeling. Je moet classes die in hetzelfde pakket vallen onder dezelfde namespace gooien. Als je dan zo'n class elders wil gebruiken doe je het gewoon zo:
uitvoeren.php:<?php
require 'include.php';
$inst = new foonamespace::Foobar;
$inst->foobar = 'foobar';
echo $inst->foobar;
?>
Let op de foonamespace::. Daarmee geef ik aan dat de class Foobar zich begeeft in de namespace foonamespace (zoals aangegeven in include.php).
Je kunt zo ook meerdere lagen diep gaan:
<?php
namespace PHPhulp::Database;
class SQLstatements
{
// iets;
}
?>
Deze class kun je dan weer later aanroepen met PHPhulp::Database::SQLstatements.
Namen korter maken
PHPhulp::Database::SQLstatements is nog steeds vrij lang, dus dat moet korter! Daar heb je de use-operator voor gekregen:
include.php:<?php
namespace PHPhulp::Database;
class SQLstatements
{
// iets;
}
?>
uitvoeren.php:<?php
use PHPhulp::Database as PHP_DB;
$inst = new PHP_DB::SQLstatements();
// etc
?>
Het PHP team heeft de operator use in eerste instantie import genoemd, maar later toch herzien naar use. Dit komt omdat import al veel wordt gebruikt als functienaam in bestaande scripts, en het nu een reserved keyword moet worden. Dat zou betekenen dat veel bestaande scripts errors gaan opleveren. En het PHP team heeft use al reserved gemaakt vanaf PHP 5 door een foutje, waardoor de upgrade van PHP 5 naar PHP 6 geen problemen kan opleveren met de use-operator (hij is namelijk al reserved).
Let op: namespace-declaraties moeten helemaal bovenaan in het document.
Bedoeling
De bedoeling is dus dat je classes binnen hetzelfde pakket in dezelfde namespace zet. Bij een framework kun je subonderdelen in subnamespaces zetten met Hoofd::Sub en die aanroepen met Hoofd::Sub::Class (of een afkorting met use). Zo creëer je packages en heb je geen botsingen meer met class-namen en je hebt geen lange class-namen meer, je hoeft ze namelijk slechts bovenaan je document te importeren en verder kun je de afkorting gebruiken. Problemen opgelost.
Pagina 5
Label en goto
In PHP 6 krijg je ook te maken met labels en goto. Je kunt in je script ergens een label aangeven, en met goto naar die label toe gaan en de rest van het script overslaan. Een voorbeeld:
<?php
$a = 5;
if ($a > 3) {
goto its_too_big;
}
$a = 3;
echo 'foobar';
its_too_big:
echo $a;
?>
Dit script zal als output hebben:
Lijkt me vrij duidelijk. Je geeft eigenlijk een bepaald punt in je script aan waar je met goto heen kan springen. Ik ben zelf niet erg gecharmeerd hiervan, maar het komt uit andere programmeertalen en is volgens veel mensen zeer nuttig (meningen hierover gewenst).
<?php
$a = 5;
if ($a > 3) {
goto its_too_big;
}
$a = 3;
echo 'foobar';
its_too_big:
echo $a;
?>
Dit script zal als output hebben:
5Lijkt me vrij duidelijk. Je geeft eigenlijk een bepaald punt in je script aan waar je met goto heen kan springen. Ik ben zelf niet erg gecharmeerd hiervan, maar het komt uit andere programmeertalen en is volgens veel mensen zeer nuttig (meningen hierover gewenst).
Pagina 6
Verkorte ifsetor() (vanaf PHP 5.3)
Je hebt nu (vóór PHP 6) de versnelde if-else syntax in PHP, namelijk:
<?php
$var = (vergelijking) ? waarde : andere_waarde;
?>
Bijvoorbeeld:
<?php
$var = 5;
$var = ($var > 3) ? 'ja' : 'nee';
// levert: ja
?>
Je hebt echter vaak de situatie dat een var alleen moet veranderen als een bepaalde variabele geset is, dus dit:
<?php
$var = 5;
$var = isset($var) ? $var : 3;
?>
Zo ben je toch weer lang aan het typen. Dit is nu versneld gemaakt. In de PHP 6 versie die ik draai werkt het echter nog niet zoals verwacht:
<?php
$var = 'foobar';
$var = $var ?: 'default';
// dit levert $var='foobar'
unset($var);
$var1 = $var ?: 'default1';
// levert default1 en notice; die notice zou niet moeten
$var = $var1 ?: 'default2';
// levert default1
?>
De notice zou niet moeten komen, want dit is een verkorte versie voor ifsetor(). Namelijk:
<?php
function ifsetor(&$input, $alternative) {
return isset($input) ? $input : $alternative;
}
$waarde = ifsetor($input, $new);
?>
Advies: nog niet gebruiken, INSTABIEL (notice)
<?php
$var = (vergelijking) ? waarde : andere_waarde;
?>
Bijvoorbeeld:
<?php
$var = 5;
$var = ($var > 3) ? 'ja' : 'nee';
// levert: ja
?>
Je hebt echter vaak de situatie dat een var alleen moet veranderen als een bepaalde variabele geset is, dus dit:
<?php
$var = 5;
$var = isset($var) ? $var : 3;
?>
Zo ben je toch weer lang aan het typen. Dit is nu versneld gemaakt. In de PHP 6 versie die ik draai werkt het echter nog niet zoals verwacht:
<?php
$var = 'foobar';
$var = $var ?: 'default';
// dit levert $var='foobar'
unset($var);
$var1 = $var ?: 'default1';
// levert default1 en notice; die notice zou niet moeten
$var = $var1 ?: 'default2';
// levert default1
?>
De notice zou niet moeten komen, want dit is een verkorte versie voor ifsetor(). Namelijk:
<?php
function ifsetor(&$input, $alternative) {
return isset($input) ? $input : $alternative;
}
$waarde = ifsetor($input, $new);
?>
Advies: nog niet gebruiken, INSTABIEL (notice)
Pagina 7
Static keyword (vanaf PHP 5.3?)
Bekijk dit stukje script:
<?php
class Foo
{
public function foobar()
{
echo 'foobar';
}
}
Foo::foobar();
?>
In PHP 5 krijg je nu te zien: foobar
In PHP 6 krijg je nu te zien:
Je probeert hier namelijk de method foobar() statisch aan te roepen met ::. Dat mag niet, want het is geen statische functie.
Zoals je ziet is PHP 6 in alles stricter geworden, dat is postief!
__callStatic()
Dit wordt de nieuwe magic method die hetzelfde werkt als __call alleen dan voor static methods.
Late static binding
In PHP 5.2 en lager heb je het volgende vervelende probleem. Bekijk dit script:
<?php
class A
{
public static function wiebenik()
{
echo __CLASS__;
}
public static function weergeefmij()
{
self::wiebenik();
}
}
class B extends A
{
public static function wiebenik()
{
echo __CLASS__;
}
}
B::weergeefmij();
?>
Je zou nu willen dat B het antwoord is, want self::wiebenik() zou de function in B moeten aanroepen, want die overschrijft A. Maar dit script geeft echter A als output.
Vanaf PHP 5.3 heb je hier een oplossing voor, namelijk static::wiebenik().
<?php
class A
{
public static function wiebenik()
{
echo __CLASS__;
}
public static function weergeefmij()
{
static::wiebenik();
}
}
class B extends A
{
public static function wiebenik()
{
echo __CLASS__;
}
}
B::weergeefmij();
?>
Zo kun je dus ook bij een static function de echte class krijgen. Normaal is dat een probleem omdat static bij het compileren anders werkt.
Bedankt Jelmer voor de hint
Al deze wijzigingen worden mogelijk in PHP 5.3 al geintroduceerd.
<?php
class Foo
{
public function foobar()
{
echo 'foobar';
}
}
Foo::foobar();
?>
In PHP 5 krijg je nu te zien: foobar
In PHP 6 krijg je nu te zien:
Strict Standards: Non-static method Foo::foobar() should not be called statically in C:\server\www6\testje.php on line 10Je probeert hier namelijk de method foobar() statisch aan te roepen met ::. Dat mag niet, want het is geen statische functie.
Zoals je ziet is PHP 6 in alles stricter geworden, dat is postief!
__callStatic()
Dit wordt de nieuwe magic method die hetzelfde werkt als __call alleen dan voor static methods.
Late static binding
In PHP 5.2 en lager heb je het volgende vervelende probleem. Bekijk dit script:
<?php
class A
{
public static function wiebenik()
{
echo __CLASS__;
}
public static function weergeefmij()
{
self::wiebenik();
}
}
class B extends A
{
public static function wiebenik()
{
echo __CLASS__;
}
}
B::weergeefmij();
?>
Je zou nu willen dat B het antwoord is, want self::wiebenik() zou de function in B moeten aanroepen, want die overschrijft A. Maar dit script geeft echter A als output.
Vanaf PHP 5.3 heb je hier een oplossing voor, namelijk static::wiebenik().
<?php
class A
{
public static function wiebenik()
{
echo __CLASS__;
}
public static function weergeefmij()
{
static::wiebenik();
}
}
class B extends A
{
public static function wiebenik()
{
echo __CLASS__;
}
}
B::weergeefmij();
?>
Zo kun je dus ook bij een static function de echte class krijgen. Normaal is dat een probleem omdat static bij het compileren anders werkt.
Bedankt Jelmer voor de hint
Al deze wijzigingen worden mogelijk in PHP 5.3 al geintroduceerd.
Reacties
0