login  Naam:   Wachtwoord: 
Registreer je!
 Nota's op tutorial:

Tutorials > PHP > Debugging
Pagina:

Reacties op de tutorial Debugging


Offline  Bart
Gepost op: 09 februari 2005 - 17:26
PHP expert

Citaat:
Bij functies als header of cookie of andere mag er NIETS uitgevoerd worden, dit betekent dat je dus voor je <? geen spatie of een enter mag zetten.
Weet je dat heel zeker dat je geen enter mag gebruiken?
Want dat doe ik namelijk wel altijd, en het werkt ook altijd?
Enig idee waarom?

Offline  twopeak
Gepost op: 09 februari 2005 - 18:25
Gouden medaille

PHP ver gevorderde


VOOR het openen van de php-tags?
Vind ik ongeloofwaardig.

Waarom? (hou je vast voor de technische uitleg)
Het HTTP protocol werkt op zo'n manier dat eerst alle headers ivm. de site ontvangen worden. Dan pas wordt de datastream (de pagina zelf) ontvangen.
Een voorbeeld van waarom dit handig is; is dat je soms wel eens kan zien hoe groot een bestand zal zijn terwijl je het download.
Zou deze informatie ergens in het midden van de pagina komen, zou het HTTP protocol nog moeten beslissen of het geevalueerd moet worden of niet.

Ik heb nu geschreven een spatie of een enter, omdat deze beiden "common mistakes" zijn.

PHP heeft hier wel een kleine "omweg" op gevonden: output buffering! (ob_start() en ob_flush())
dwz. dat PHP vanaf het inroepen van ob_star() alle informatie opvangt ipv door te sturen naar de client; de headers eruit sorteert en bij ob_flush() de informatie dan in de juiste volgorde naar de client stuurt.
Maar dan moet je er weer rekening mee houden dat je ob_start() helemaal bovenaan moet staan: anders zal er al uitvoer zijn naar de client gestuurd zijn.

Moraal van het verhaal:
Headers MOETEN eerst komen; dan pas de pagina.
Output buffering kan je helpen, maar moet eveneens helemaal bovenaan staan.

Offline  Tuinstoel
Gepost op: 29 december 2011 - 22:37
PHP expert

Nog even een aanvulling op debuggen:

Je kunt nu gewoon echt 'debuggen' in PHP met behulp van XDebug (http://http://xdebug.org/). Dus lekker breakpointen en dergelijke :-).

Offline  Thomas
Gepost op: 01 januari 2014 - 20:13
Moderator

Enkele aanvullingen naar aanleiding van de tutorial en eigen ervaring:

Over het weergeven van fouten
error_reporting() geeft aan wat er aan foutmeldingen (welke graad) weergegeven moet worden, maar niet hoe deze weergegeven moeten worden, dit wordt bepaald door de PHP-setting "display_errors". Deze moet op "true" staan (PHP < 5.2.4) of "stdout" (PHP >= 5.2.4) om deze op het scherm af te drukken. Dit is ook de standaard waarde van deze instelling, maar in de praktijk kan hier van afgeweken worden. Als je dus helemaal geen foutmeldingen op je scherm terugziet, grote kans dat dat hier aan ligt.

Over het dumpen van variabelen
Ook dit moet veilig gebeuren, want dit kan user input bevatten, of HTML, of een combinatie van beide. Stel bijvoorbeeld dat een variabele JavaScript bevat die document.cookie naar een extern adres stuurt. Op die manier kan je sessie-id of andere informatie gestolen worden.
print_r() gebruiken om waarden te dumpen is een snelle en gemakkelijke manier, maar niet erg veilig (en ook niet echt handig als deze HTML bevat, deze zie je dan niet als code (enkel het resultaat van de toegepaste HTML opmaak) en JavaScript wordt dus gewoon uitgevoerd).
Eigenlijk zou je (zoals altijd) je output moeten escapen, zo ook bij het dumpen van variabelen, bijvoorbeeld met een Debug klasse:

  1. <?php
  2. class Debug
  3. {
  4. protected static function escape($text) {
  5. return htmlspecialchars($text, ENT_QUOTES);
  6. }
  7.  
  8. protected static function clean($in) {
  9. if (is_array($in)) {
  10. // Because we might introduce new (or overwrite existing) keys when we escape them,
  11. // we build a new array $out instead of overwriting $in when it concerns an array.
  12. $out = array();
  13. foreach ($in as $k => $v) {
  14. $out[self::escape($k)] = self::clean($v); // $v might be anything, call clean() again
  15. }
  16. $in = $out;
  17. } elseif (is_numeric($in)) {
  18. $in = '(<span style="color: #ff0000;">numeric</span>) '.$in; // no cleaning necessary
  19. } elseif (is_bool($in)) {
  20. $in = '(<span style="color: #ff0000;">bool</span>) '.($in ? 'true' : 'false'); // print boolean value as string
  21. } elseif (is_object($in)) {
  22. // Convert object to string using print_r.
  23. $in = '(<span style="color: #ff0000;">object</span>) <span style="color: #009900;">'.self::escape(print_r($in, true)).'</span>';
  24. } elseif (is_null($in)) {
  25. $in = '(<span style="color: #ff0000;">null</span>)';
  26. } elseif (is_resource($in)) {
  27. $in = '(<span style="color: #ff0000;">resource</span>)'; // @todo add more info?
  28. } elseif (is_string($in)) {
  29. $in = '(<span style="color: #ff0000;">string</span>) '.self::escape($in);
  30. } else {
  31. // something we missed
  32. $in = '(<span style="color: #ff0000;">something</span>)';
  33. }
  34. return $in;
  35. }
  36.  
  37. public static function dump($in, $file='', $line='') {
  38. if (!empty($line) && !empty($file)) {
  39. echo '<b>variable dump in file '.$file.' on line '.$line.'</b>';
  40. } else {
  41. echo '<b>variable dump</b>';
  42. }
  43. if (is_array($in)) {
  44. echo '<pre>'.print_r(self::clean($in), true).'</pre>';
  45. } else {
  46. echo '<pre>'.self::clean($in).'</pre>';
  47. }
  48. }
  49. }
  50. ?>


Voorbeeld van aanroep:
  1. <?php
  2. // Test class.
  3. class Test
  4. {
  5. protected $test;
  6.  
  7. public function __construct($test) {
  8. $this->test = $test;
  9. }
  10. }
  11.  
  12. $test = array(
  13. 'a' => array( // subarray
  14. 1 => "<script>alert('hoi')</script>", // nasty code
  15. 2 => false, // a boolean
  16. 77 => true, // another boolean
  17. 3 => '5.6666667', // a numeric value stored as string
  18. 'object' => new Test('<b>hallo</b>'), // an object
  19. 4 => 1.2, // a numeric value (float)
  20. 5 => 7, // a numeric value (integer)
  21. 6 => array( // subsubarray
  22. 'z' => '<b>bold</b>', // more nasty code
  23. 'a' => 'asdf', // a string
  24. '<la' => 'hai', // nasty key
  25. ),
  26. 9 => null,
  27. ),
  28. '<b>' => 'lala<br />', // key with code in it
  29. '&lt;b&gt;' => 'hoi', // escaped variant of previous key, which should not be overwritten by your dump functionality...
  30. );
  31. Debug::dump($test, __FILE__, __LINE__);
  32. ?>


Door het meegeven van __FILE__ en __LINE__ kun je gemakkelijk terugvinden waar je je dump-routine aanriep, maar het is niet verplicht om deze variabelen mee te geven.

Dan enkele "gotchas" waar ik wel eens tegenaan liep:
= in plaats van ==(=)
Stel je hebt de volgende code:
  1. <?php
  2. $test = true;
  3. if ($test = false) {
  4. echo 'Deze tekst zou afgedrukt moeten worden.';
  5. }
  6. ?>

Het resultaat van een toekenning (a = b) wordt in een if-statement geevalueerd en is gelijk aan de waarde van de toekenning (b), dus in het fragment hierboven staat if(false) en dat levert dus nooit iets op.
Een ezelsbrug om dit te voorkomen is dat je de constante (false) altijd vooraan zet in een vergelijking, en dan pas je variabele ($test).
Je krijgt dan dus zoiets:
  1. <?php
  2. $test = true;
  3. if (false = $test) {
  4. echo 'Dit wordt dus nooit afgedrukt...';
  5. }
  6. ?>

En dat levert een (syntax-)foutmelding van PHP op, terwijl het vorige fragment wel gewoon werkt (maar dus fout is). Met deze ezelsbrug in het achterhoofd hoef je het dus eigenlijk al niet meer in de vorm <constante> == <variabele> te schrijven, omdat je de controle zelf al automatisch uitvoert . Nog beter is wellicht een type controle ($test === false).

Het controleren of een waarde bestaat in combinatie met null-waarden
  1. <?php
  2. $test['a'] = null;
  3. if (isset($test['a'])) {
  4. echo 'hoi';
  5. }
  6. ?>

In het bovenstaande wordt geen 'hoi' afgedrukt. Als je waarden wilt initialiseren "zonder waarde" is false mogelijk een betere keuze dan null, omdat isset dan wel "true" oplevert:
  1. <?php
  2. $test['a'] = false;
  3. if (isset($test['a'])) {
  4. echo 'hoi';
  5. }
  6. ?>


Dan enkele tips die mogelijk handig kunnen zijn:

Stop je query in een string
Omdat SQL foutmeldingen niet altijd even informatief zijn kan het handig zijn om in plaats van dit (in zijn eenvoudigste vorm dan he):
  1. <?php
  2. $res = mysql_query('SELECT ... FROM ... WHERE ...') or die(mysql_error());
  3. ?>


Het volgende te doen:
  1. <?php
  2. $query = 'SELECT ... FROM ... WHERE ...';
  3. $res = mysql_query($query) or die(mysql_error().': '.htmlspecialchars($query, ENT_QUOTES));
  4. ?>


En ook hier niet vergeten te escapen, zowel de variabelen in de query zelf alsmede de gedumpte output!

Errorlogs zijn je Beste Vriend
En als je de fout niet kunt vinden, raadpleeg dan je (Apache) errorlogs. Ook is het geen slechte houding om deze sowieso geregeld eens na te lopen om te zien wat er allemaal op je site misgaat. Dit helpt het voeden van je achterdocht waarschijnlijk ook, en zal je verder aanmoedigen heel netjes (en veilig!) te programmeren .

Ik zie alleen een wit scherm?
Grote kans dat je met een geheugen-intensieve operatie bezig was en je geheugen is uitgeput. Soms (maar lang niet altijd!) maakt je (Apache)log melding van een memory limit die overschreden wordt.

Ik heb mijn geheugenlimiet opgehoogd maar er gebeurt niets?
Kijk met phpinfo() of jouw versie Suhosin gebruikt. Dit is een extra security module die er volgens mij voornamelijk voor zorgt dat je leven miserabeler is, maar het zal vast ook een paar nuttige dingen doen. Suhosin zorgt ervoor dat je niet @runtime kunt schuiven met de geheugenlimiet. De makkelijkste manier op dit op te lossen is het (zelf of door systeembeheer) in Suhosin laten instellen van het maximale geheugenplafond. Daarna kun je @runtime schuiven met je geheugen tot (en met) dit maximale geheugenplafond.

POST en GET variabelen zijn ineens leeg?!
Zie vorig puntje, ook hier kan Suhosin de boosdoener zijn als je veel GET/POST variabelen hebt en/of deze lange variabele-namen hebben. Suhosin heeft voor al dat soort zaken limieten...

Pagina:

Enkel aanvullende informatie is welkom. Geen prijzende of afkeurende reacties.
 
© 2002-2024 Sitemasters.be - Regels - Laadtijd: 0.319s