Programming & Webscripting FAQ: PHP
Links
- http://www.php.net/functienaam
Voor snelle referentie aan een functie in de PHP manual
- http://www.php.net/nl/functienaam
Idem, in het Nederlands
- http://www.php.net
Alles over PHP
- PHP tiplist
- Functionaliteit van php
- http://www.zend.com
- http://www.weberdev.com
- http://www.phpbuilder.com
- http://www.devshed.com
- http://sourceforge.net/projects/phptriad
Sourceforge project voor snelle installatie van de combo "Apache-PHP-MySQL" op je Windows systeem
- http://www.firepages.com.au/
Nog een snelle installer voor AMP-systemen
- http://www.phpdeveloper.org/
- http://www.securereality.com.au/studyinscarlet.txt
Veiligheid op PHP-based sites (site uit 2001)
- http://www.phppatterns.com/
Boeken
- Beginning PHP4
Door: John Blank, WROX author team
ISBN: 1861003730
Wrox press - PHP4 zonder stress
Door: Jochen Franke
ISBN: 9051674643
Easy Computing - MySQL/PHP Database Applications
Door: Brad Bulger, Jay Greenspan
ISBN: 0764535374
Academic Service - PHP Bible
Door: Joyce Park, Tim Converse
ISBN: 0764549553
John Wiley and Sons Ltd - PHP 4 het complete Handboek
Door: Tim Converse & Jouce Park
ISBN: 9039516103
Academic service
FAQ
- Mijn POST en GET forms werken niet (meer)
- Ik kan de fout echt niet vinden!
...en het "waarom?" van error_reporting
- Hoe zet ik bepaalde configuratie directives aan/uit?
- "... is not a valid MySQL resource index"
- Mijn sessie-variabelen worden niet opgeslagen
- Mijn sessie-variabelen verdwijnen na sluiten van het browservenster
- Mijn file-upload form werkt niet.
- Ik heb een multiple select of meerdere checkboxes, maar krijg maar 1 variabele binnen in PHP?
- Van mijn <input type="image" ... > kan ik de coordinaten niet terugvinden?
- Bij een javascript-submit of drukken op enter wordt mijn form niet herkend als 'submitted'?
- Spaties van een <input ... value=...> worden niet herkend door de browser
- 'Escapen' van quotes in queries?
Ook: waarom krijg ik meerdere slashen in mijn database?
- Ik wil index.php?piet=1&sjaak=3 vervangen door index/1/3. Hoe doe ik dat?
- Het bewerken van afbeeldingen in PHP, hoe werkt dat?
Mijn POST en GET forms werken niet (meer)
Hoogstwaarschijnlijk ben je onlangs gemigreerd van een oude PHP versie naar een nieuwe, of je bent begonnen aan een PHP tutorial die verouderd is.
In de manual van PHP vind je onder het stukje Predefined variables e.e.a. over de zogenaamde "superglobals", die in versie 4.1.2 hun intrede deden. Daarnaast werd in versie 4.2.0 de configuration directive 'register_globals' standaard op Off gezet. Dit had als gevolg dat variabelen die van buitenaf komen niet meer automagisch globaal aangemaakt werden.
1
2
3
| <? echo 'Welkom ' . $user . ' op mijn coole pagina!'; ?> |
Dit stukje php werkte voorheen altijd gewoon zonder problemen, als je het aanriep met index.php?user=drm. Tegenwoordig zijn de variabelen terug te vinden in hun respectievelijke superglobals.
1
2
3
| <? echo 'Welkom ' . $_GET [ 'user' ] . ' op mijn coole pagina!'; ?> |
Zie verder de manual voor het gebruik van deze superglobals.
Zie ook dit artikel van Jason E. Sweat voor hulp bij het programmeren met register_globals op Off.
Ik kan de fout echt niet vinden!
Heb je error_reporting op E_ALL staan? Nee? Meteen doen! Deze directive / functie op E_ALL zetten zorgt er namelijk voor dat je veel meer bruikbare debug-informatie krijgt. PHP is namelijk van zichzelf heel erg tolerant met bepaalde constructies, terwijl ze eigenlijk niet helemaal netjes zijn of al min of meer verboden zijn maar nog toegelaten worden wegens backward compatibility.
Als je zgn. notices ook toelaat in je error_reporting krijg je o.a. informatie over
- Het gebruik van niet-bestaande variabelen of elementen uit een array
Om te controleren of een bepaalde variabele of element bestaat gebruik je de functie isset().
Vooral zinvol bij tikfouten en controleren op malafide input
- Het gebruik van ongequote strings als array-indices
Ongequote strings worden enkel nog toegelaten bij wijze van backward compatibility. In principe is dit uit de taal geschrapt vanwege mogelijke conflicten met constanten gedefinieerd met define()
De manual zelf omschrijft het als volgt:
Notices are not printed by default, and indicate that the script encountered something that could indicate an error, but could also happen in the normal course of running a script. For example, trying to access the value of a variable which has not been set, or calling stat() on a file that doesn't exist.
Schrik dus niet als je je error_reporting ook op E_ALL hebt staan
Hoe zet ik bepaalde configuratie directives aan/uit?
Vaak komt het voor dat je zelf maar een klein stukje webspace hebt, maar niet alle opgelegde configuraties van de serverbeheerder zo wilt zoals hij ze heeft ingesteld.
Zoek in de manual op welke directive je aan wilt passen, en ga na op welke manieren je ze kunt zetten.
Je vindt ook een tabel terug waarin staat wat de PHP_INI_* waarden betekenen:
PHP_INI_x waarde | Als deze erbij staat, betekent dat dat je de waarde kunt setten in: |
---|---|
PHP_INI_USER | je script dmv. de functie ini_set(). |
PHP_INI_PERDIR | php.ini, .htaccess of httpd.conf. |
PHP_INI_SYSTEM | php.ini of httpd.conf |
PHP_INI_ALL | al van bovenstaande mogelijkheden |
Zie voor meer informatie dit stuk uit de manual.
... is not a valid MySQL resource index
Hoogstwaarschijnlijk heb je een fout staan in je MySQL query. Gebruik de functie mysql_error() om erachter te komen wat de fout precies is.
Een goede gewoonte is het om bij mysql queries altijd even te kijken of er een fout is opgetreden, en zo ja, wat de fout en de query dan was:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <? $query = " SELECT woei FROM table WHERE woei_id={$_REQUEST [ 'id']} "; mysql_query ( $query ); if ( mysql_errno () > 0 ) { trigger_error ( "MySQL Error!!<br><br>" . mysql_error () . "<br>in query:<br>$query" ); } ?> |
Zie voor meer informatie over het effectief gebruik van trigger_error() dit stukje.
Meer informatie over mysql functies vind je hier.
Mijn sessie-variabelen worden niet opgeslagen
Gebruik session_register () en $_SESSION constructies niet door elkaar.
session_register ()
1
2
3
4
5
6
7
8
9
10
| <? session_start (); session_register ( 'blaat' ); if ( !session_registered ( 'blaat' ) ) { $blaat = "Blaat"; } else { echo 'Blaat was reeds gedefinieerd: ' . $blaat; } ?> |
$_SESSION
1
2
3
4
5
6
7
8
9
| <? session_start (); if ( !isset ( $_SESSION [ 'blaat' ] ) ) { $_SESSION [ 'blaat' ] = 'Blaat'; } else { echo 'Blaat was reeds gedefinieerd: ' . $_SESSION [ 'blaat' ]; } ?> |
Gebruik of de één, of de ander.
Als je beide constructies door elkaar gebruikt, werkt het niet. As simple as that.
Sterker nog: sinds register_globals default uit staat, is het gebruik van session_register deprecated:
Caution
If you want your script to work regardless of register_globals, you need to instead use the $_SESSION array as $_SESSION entries are automatically registered. If your script uses session_register(), it will not work in environments where the PHP directive register_globals is disabled.
Mijn sessie-variabelen verdwijnen na sluiten van het browservenster
Uit http://www.php.net/manual/en/ref.session.php:
session.cookie_lifetime specifies the lifetime of the cookie in seconds which is sent to the browser. The value 0 means "until the browser is closed." Defaults to 0.
See also session_get_cookie_params() and session_set_cookie_params().
Uit http://www.php.net/manual/en/function.session-set-cookie-params.php:
void session_set_cookie_params ( int lifetime [, string path [, string domain [, bool secure]]])
Set cookie parameters defined in the php.ini file. The effect of this function only lasts for the duration of the script.
Een stukje code bovenaan elk script zou je sessie dus 30 dagen actief moeten houden:
1
| session_set_cookie_params(30*24*60*60); |
Let daarbij er dus op, dat een lifetime van 0 betekent dat het cookie getrashed wordt bij sluiten van het browservenster.
Mijn file-upload form werkt niet.
- Controleer of je form-tag juist gedefinieerd hebt:
method moet "post" zijn
enctype moet "multipart/form-data" zijn
Als je de enctype niet goed zet, komt de waarde van het <input type="file"> terug in $_POST, anders kun je hem terugvinden in $_FILES.
- Controleer de php-configuratie
Controleer de directives file_uploads, upload_max_filesize, upload_tmp_dir en post_max_size op hun waarden.
Ik heb een multiple select of meerdere checkboxes, maar krijg maar 1 variabele binnen in PHP
Je kunt door achter de naam van je input-veld [] te zetten aan PHP vertellen dat je graag wilt dat alle variabelen die met die naam binnen komen als array behandeld moeten worden:
Voorbeeld
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <form method="get"> <input type="checkbox" id="my_check_1" name="my_check[]" value="first_check" /> <label for="my_check_1">First check</label><br /> <input type="checkbox" id="my_check_2" name="my_check[]" value="second_check" /> <label for="my_check_2">Second check</label><br /> <input type="checkbox" id="my_check_3" name="my_check[]" value="third_check" /> <label for="my_check_3">Third check</label><br /> <select name="my_select[]" multiple="multiple"> <option value="first_option">First option</option> <option value="second_option">Second option</option> <option value="third_option">Third option</option> </select> <input type="submit" /> </form> |
Het gevolg van een dergelijk formulier is dat in dit geval de $_GET array binnen PHP op zijn beurt weer arrays bevat waarin aangegeven staat welke opties 'aangevinkt' zijn:
Uitvoer
1
| print_r ( $_GET ) |
1
2
3
4
5
6
7
8
9
10
11
12
13
| Array ( [my_check] => Array ( [0] => first_check [1] => second_check ) [my_select] => Array ( [0] => second_option [1] => third_option ) ) |
Eveneens kun je de indices van de array op zichzelf ook namen/nummering geven. Als je dat doet kun je eenvoudig de 'value' van checkboxes achterwege laten:
1
2
3
4
5
6
| <input type="checkbox" id="my_check_1" name="my_check[first]" /> <label for="my_check_1">First check</label><br /> <input type="checkbox" id="my_check_2" name="my_check[second]" /> <label for="my_check_2">Second check</label><br /> <input type="checkbox" id="my_check_3" name="my_check[third]" /> <label for="my_check_3">Third check</label><br /> |
Uitvoer:
1
| print_r ( $_GET ) |
1
2
3
4
5
6
7
8
| Array ( [my_check] => Array ( [first] => on [second] => on ) ) |
Met een beetje creatief gebruik van in_array () en/of foreach constructies is het op die manier heel eenvoudig te achterhalen welke opties aangevinkt zijn.
Van mijn <input type="image" ... > kan ik de coordinaten niet terugvinden?
Zoals je in de PHP.net FAQ kunt lezen, worden de namen foo.x en foo.y variabelen in PHP omgezet naar resp. foo_x en foo_y.
Bij een javascript-submit of drukken op enter wordt mijn form niet herkend als 'submitted'?
Als je in je form een <input type="submit"> een name en een value geeft, worden deze in Internet Explorer niet meegegeven als form-waarde.
Je kunt dan beter 1 van de 2 volgende constructies gebruiken om te controleren of je form gesubmit is:
In het geval van een POST formulier:
1
2
3
4
5
6
7
| <? if ( strcasecmp ( $_SERVER [ 'REQUEST_METHOD' ], 'post' ) == 0 ) { // het post-formulier is verstuurd } else { // het is een 'gewone' (GET-)aanroep. } ?> |
In het geval van een GET formulier:
Uiteraard kun je deze wijze ook toepassen bij een POST. Het is maar net wat jouw voorkeur geniet.
1
2
3
4
5
| <form ...> <input type="hidden" name="submitted" value="true" /> .... </form> |
1
2
3
4
5
| <? if ( isset ( $_GET [ 'submitted' ] ) && $_GET [ 'submitted' ] == 'true' ) { // ... } ?> |
Spaties van een <input ... value=...> worden niet herkend door de browser
Je moet attributen van een html element om deze reden altijd quoten en de waarde ervan escapen. Zie de volgende voorbeelden:
Waarom quoten?
1
2
3
4
5
6
7
| <? $my_value = "This is the value of the field"; echo '<input type=text name=text_input value='; echo $my_value; echo '>'; ?> |
Het resultaat is dan:
1
| <input type=text name=text_input value=This is the value of the field> |
De browser zal enkel het stukje This als waarde van het veld interpreteren. De rest zal de browser proberen als attributen te behandelen. Als dat niet lukt wordt de informatie gewoon genegeerd. Als je echter quotes om de waarde heenzet, heb je daar geen last van.
Waarom escapen?
1
2
3
4
5
6
7
| <? $my_value = 'This is a value with a "quoted" part'; echo '<input type="text" name="text_input" value="'; echo $my_value; echo '">'; ?> |
Het resultaat is dan:
1
| <input type="text" name="text_input" value="This is a value with a "quoted part> |
Je zal begrijpen dat de browser dit niet anders kan interpreteren dan dat het gedeelte This is value with a de waarde van het attribuut value is.
Gebruik dus htmlspecialchars() of htmlentities() om de values te escapen.
Escape problemen met PHP i.c.m. databases
Controleer altijd of get_magic_quotes_gpc() en get_magic_quotes_runtime() aan staan. Als 1 van deze twee aan staat, worden variabelen binnen PHP die enkele (') of dubbele (") quotes of backslashes (\) bevatten automagisch escaped. Dat wil zeggen dat een string die in PHP binnen komt via GET, POST of een cookie (gpc) die een ', " of \ bevat automagisch een \ voor die character krijgt. Bekijk het volgende voorbeeld:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <form method="get"> <input name="test" type="text" value="Test: ', " and \" /> <input type="submit" /> </form> <pre> <? if ( get_magic_quotes_gpc () ) { echo 'Magic quoting with get, post or cookie is enabled', "\n"; } print_r ( $_GET ); ?> </pre> |
Uitvoer met get_magic_quotes_gpc aan, na submit:
1
2
3
4
5
| Magic quoting with get, post or cookie is enabled Array ( [test] => Test: \', \" and \\ ) |
Daarbij is het dus overbodig om nog een addslashes of een andere escape-functie los te laten op die variabelen.
Houd zelf de controle op het escapen
Het is het overwegen waard om zelf de controle te houden op het escapen van variabelen, om te voorkomen dat je bijvoorbeeld in <input ...> velden een ongewenste backslash krijgt.
Mijn voorkeur heeft het dan ook om in de code te checken of er al ge-escaped is, en zo ja: die escape characters weer te verwijderen:
Uiteraard is het ook een optie om je configuratie helemaal naar je hand te zetten, maar je kunt een reden om dat niet globaal te doen en de magic quotes directives kun je niet altijd per script aan of uit zetten, behalve magic_quotes_runtime (zie set_magic_quotes_runtime (). Daarnaast is het het overwegen waard om je scripts zo portable mogelijk te maken (onafhankelijk van configuraties)
1
2
3
4
5
6
7
8
9
10
11
| <? set_magic_quotes_runtime ( 0 ); if ( get_magic_quotes_gpc () ) { foreach ( $_POST as $key => $value ) { $_POST [ $key ] = stripslashes ( $value ); } foreach ( $_GET as $key => $value ) // etcetera ... } ?> |
Vervolgens kun je met je eigen escape functies de juiste escapes doen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| define ( 'QUOTE_SINGLE', 39 ); define ( 'QUOTE_DOUBLE', 34 ); define ( 'QUOTE_BACKTICK', 96 ); function mysql_escape ( $string, $quote_char_code = QUOTE_SINGLE ) { $quote_char = chr ( $quote_char_code ); return $quote_char . str_replace ( array ( '\\', $quote_char ), array ( '\\\\', '\\' . $quote_char ), $string ) $quote_char; } // .... $query = " INSERT INTO table(fieldname) VALUES( " . mysql_escape_string ( 'Dit " is \' een string \\ met wat \ bullshit \\\' ' ) . " ); "; |
Overigens is het ook te overwegen de ANSI-SQL wijze van escapen te hanteren, door een enkele quote te escapen met een extra enkele quote:
1
2
3
| function ansi_sql_escape ( $string ) { return str_replace ( "'", "''", $string ); } |
De testcase voor werking van je escapes:
Om goed te testen of je escaping wel goed werkt, probeer het volgende in te voeren in je database door middel van een formuliertje:
1
| ' \' '' \\' \\\' " \" "" \\" \\\" ` \` `` \\` \\\` |
Wanneer je bij inserten van deze string geen foutmelding krijgt en je krijgt exact dezelfde string terug bij het uitlezen van die string uit je database, werkt je escaping perfect:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| if ( isset ( $_GET [ 'string' ] ) ) { $query = ' INSERT INTO table(fieldname) VALUES( ' . mysql_escape ( $_GET [ 'string' ] ) . ' ); '; mysql_query ( $query ) or trigger_error ( "Fout: " . mysql_error () . "<br>query: " . $query ); $id = mysql_insert_id (); $query = ' SELECT fieldname FROM table WHERE field_id=' . $id; $res = mysql_query ( $query ) or trigger_error ( "Fout: " . mysql_error () . "<br>query: " . $query ); $selected_field = mysql_result ( $res, 0, 0 ); echo 'inserted: ', $_GET [ 'string' ], '<br>'; echo 'retrieved: ', $selected_field, '<br>'; echo 'ok?: ', $selected_field == $_GET [ 'string' ], '<br>'; } ?> <form method="get"> <input type="text" name="string" /> <input type="submit" /> </form> |
Maar waarom gebruik je nou in vredesnaam die add- en stripslashes functies niet?
Het probleem met die functies is dat er soms dingen escaped worden die niet ge-escaped hoeven te worden naast dingen die wel escaped moeten worden. Als er dus data ingevoerd die opzichzelf een escape-sequence is, wordt die data in eerste instantie escaped voor bijv. MySQL, maar bevat hij bij uitlezen weer diezelfde escape-sequence die ingevoerd is. Als je daar vervolgens een stripslashes () overheenhaalt, ben je een escape sequence kwijt. Vandaar dat de bovengenoemde testcase de beste is om na te gaan of er niet teveel of te weinig escapes uitgevoerd worden
Ik wil index.php?piet=1&sjaak=3 vervangen door index/1/3. Hoe doe ik dat?
Eigenlijk is dit niet echt een PHP vraag, maar een Apache vraag. Maar op de een of andere manier komt het altijd in een PHP context weer terug, vandaar hier maar een stukje uitleg
MultiViews
Verreweg de meest eenvoudige manier is om in je Apache configuratie (hetzij in httpd.conf, hetzij in een .htaccess, hetzij nog ergens anders) voor de directory waar je je php'tje hebt staan de MultiViews option aan te zetten:
1
| Options +MultiViews |
De Apache 1.3 documentatie zegt er het volgende over:
The effect ofMultiViews
is as follows: if the server receives a request for/some/dir/foo
, if/some/dir
hasMultiViews
enabled, and/some/dir/foo
does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name. It then chooses the best match to the client's requirements.
In PHP kun je vervolgens de binnengekomen request afhandelen met de $_SERVER [ 'REQUEST_URI' ] variabele.
mod_rewrite
Een veel ingewikkelder maar veel flexibeler oplossing is het gebruik maken van mod_rewrite. Deze module geeft je de beschikking over diverse configuratie directives die vertellen hoe een inkomende request "vertaald" (rewritten) moet worden naar een door jou te bepalen URI.
Een eenvoudig voorbeeld is om bijvoorbeeld de url /rewrite-test/index/1/2 te rewriten naar /rewrite-test/index.php?piet=1&sjaak=2
1
2
3
| RewriteEngine On RewriteBase /rewrite-test/ RewriteRule ^index/([0-9]+)/([0-9]+)$ index.php?piet=$1&sjaak=$2 |
Je kunt dit natuurlijk zo gek maken als je zelf wilt. Een goed opgezette set rewriterules kan ook zorgen voor goede 'default'-afhandeling en shortcuts naar bepaalde pagina's. Zo zou je bijvoorbeeld een rewriterule kunnen maken die doorverwijst naar een search-engine als een url niet bestaat, of /user/piet doorverwijzen naar een profile.php?user=piet, etcetera. Alles bij elkaar genomen is het zeer zeker de moeite waard mod_rewrite te verkiezen boven MultiViews.
Voor meer informatie over het gebruik van mod_rewrite kun je het beste de URL Rewriting Guide en de mod_rewrite reference van Apache doornemen.
Voor meer informatie over het gebruik van regular expressions kun je hier terecht.
[ Voor 171% gewijzigd door NMe op 15-05-2005 02:26 ]
Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz