1. Dec 24
    Lenkebrudd

    Lenkebrudd

    Dette skriveriet er løst basert på en ALA artikkel jeg leste her om dagen. Den omhandler hvordan du kan lage en feilmeldingsside som er mer informativ både til deg og brukeren som opplever den. ALA forfatteren har valgt å dele inn forskjellige error 404-meldinger i 4 grupper. Jeg derimot velger å sette disse til 3 for enkelhetens skyld.

    • Feil fra brukerens side. Gammelt bokmerke, feilskreven URL.
    • Et brudd på en lenke på din side.
    • Et brudd på en lenke fra en annen side. Enten en søkeside eller en annen side.

      Hovedsakelig ønsker vi som utvikler og eier av siden informasjon dersom det er brudd på en lenke i din side, men også muligens fra andres sider. Dersom det er noen som lenker til siden din (kanskje en annen blogg) så kan du sende en e-post til de og informere om lenkebruddet. Gammelt bokmerke eller feil skrevet URL trenger vi ikke så mye informasjon om.

      For å finne hvor brukeren kom fra kan vi bruke superglobale tabellen _SERVER og hente ut verdien av HTTP_REFERER. Det er viktig å huske at HTTP_REFERER ikke er 100% nøyaktig. Den fungerer bare dersom det blir linket igjennom en a-tag, eller igjennom form-submitt og bilde-kilde. Det fungerer altså ikke dersom det er linket igjennom en e-post, med en redirect, lasting igjennom XML, bokmerke og lignende. Det er også ganske lett å endre innholdet i HTTP_REFERER, så dersom den skal skrives ut må vi ta høyde for XSS. Siden dette ikke er en veldig essensiell funksjon vi skal lage, holder HTTP_REFERER lenge som en pekepinn på hvor brukeren kom i fra. Det er også ingen reell trussel for XSS siden vi ikke trenger å skrive ut verdien eller bruke den i en database eller lignende. HTTP_REFERER kan også ikke eksistere i enkelte tilfeller, derfor er det lurt å ta en isset()-test når man setter verdien.

      Vi setter verdien av HTTP_REFERER slik:

      $referer = null;
      if(isset($_SERVER['HTTP_REFERER'])) {
          $referer = $_SERVER['HTTP_REFERER'];
      }

      1. Gammelt bokmerke eller feilskreven URL

      Dersom $referer er null går vi ut i fra at det går under kategori 1. Vi har ingen stor nytteverdi av å vite dette i noen slags logg eller på e-post, derfor skriver vi bare ut til brukeren at de burde oppdatere bokmerkene sine, eller se etter korrekt side ved å bruke søketjenesten vi har på siden.

      echo "
      <h2>Feilmelding: 404 Fil ikke funnet!</h2>
      \n";
      echo "
       
      Filen du letet etter finnes ikke her. 
       
      ";
      if($referer === null) {
      	echo "Det ser ut til at du bruker et gammelt
      	bokmerke eller har skrevet inn feil URl.
       
      Prøv søkefeltet på siden eller skriv inn
      korrekt URL i adressefeltet i nettleseren din.\n";
      }

      Vi skriver bare ut en enkel feilmelding til brukeren, og logger ikke eller noe i den dur.

      2. Et brudd på en lenke på din side

      Dersom det er et brudd i en lenke på vår side vil vi gjerne vite om det for å kunne korrigere feilen. Derfor kan vi velge å i tillegg til å skrive ut feilmelding så logger vi hendelsen og sender en e-post til administrator om hva som har skjedd.

      Dersom vårt domenenavn er i $referer går vi ut i fra at det er en brudd i en lenke på siden vår. Det er ikke krise dersom noen har tuklet med HTTP_REFERER og lagt inn domenenavnet vårt. Det fører bare til en e-post som vi kan sjekke kjapt selv. Jeg tar en enkel test her for å vise konseptet.

      if (stristr($referer, $_SERVER['SERVER_NAME'])) {
      	sendMailError404("BROKEN_LOCAL", $referer);
      	logError404("BROKEN_LOCAL", $referer);
       
      	echo "Det ser ut til at vi har et lenkebrudd på siden
      	vår. Vi vil med dette se nærmere på saken og håper
      	å få fikset det i løpet av kort tid.
       
              Prøv søkefeltet på denne siden for å finne
      	en annen link til denne siden.\n";
      }

      Her bruker jeg to pseudo-funksjoner til å vise til hvordan en kan gjøre det. sendMailError404 sender rett og slett e-post til administrator som informerer om at det er et lenkebrudd på siden som $referer viser til. Loggen lagrer bare samme informasjon i en txt-fil på root-området eller lignende.

      3. Et brudd på en lenke fra en annen side

      Ved øvrig hendelser, hvor referer er satt men ikke inneholder domenenavnet ditt kan man gå ut i fra at det er en annen side som har en brutt lenke til siden din. Her er det kanskje ikke nødvendig å sende inn en e-post hver gang dette skjer. Ofte kan det være slik at søkesider har gamle sider indeksert og da kan det være litt plagsomt å få flere e-poster hver dag med informasjon om lenkebrudd fra f.eks Google. I dette tilfellet ville jeg bare ha logget hendelsen og med jevne mellomrom sjekket loggen etter noen lenkebrudd fra f.eks bloggrollen min eller andre sider.

      else {
      	logError404("BROKEN_EXTERNAL", $referer);
      	echo "Det ser ut til at vi har et lenkebrudd på en
      	ekstern side. Vi vil med dette se nærmere på saken og håper
      	å få fikset det i løpet av kort tid.
       
              Prøv søkefeltet på denne siden for å finne
      	en annen link til denne siden.\n";
      }

      Vi kan sette en komplett semi-kode på dette for å vise til hvordan det kan gjøres.

      $referer = null;
      if(isset($_SERVER['HTTP_REFERER'])) {
          $referer = $_SERVER['HTTP_REFERER'];
      }
       
      echo "
      <h2>Feilmelding: 404 Fil ikke funnet!</h2>
      \n";
      echo "
       
      Filen du letet etter finnes ikke her.
       
      ";
       
      if($referer === null) {
      	echo "
       
      Det ser ut til at du bruker et gammelt
      	bokmerke eller har skrevet inn feil URl.
       
      Prøv søkefeltet på siden eller skriv inn
      	korrekt URL i adressefeltet i nettleseren din.
       
      \n";
       
      } else if (stristr($referer, $_SERVER['SERVER_NAME'])) {
      	sendMailError404("BROKEN_LOCAL", $referer);
      	logError404("BROKEN_LOCAL", $referer);
       
      	echo "
       
      Det ser ut til at vi har et lenkebrudd på siden
      	vår. Vi vil med dette se nærmere på saken og håper
      	å få fikset det i løpet av kort tid.
       
      Prøv søkefeltet på denne siden for å finne
      	en annen link til denne siden.
       
      \n";
      } else {
      	logError404("BROKEN_EXTERNAL", $referer);
      	echo "
       
      Det ser ut til at vi har et lenkebrudd på en
      	ekstern side. Vi vil med dette se nærmere på saken og håper
      	å få fikset det i løpet av kort tid.
       
      Prøv søkefeltet på denne siden for å finne
      	en annen link til denne siden.
       
      \n";
      }

      Sette en PHP-fil som feilmeldingsdokument

      Når man har laget et PHP-dokument som inneholder koden over må man sette filen man lagrer det som til en Error 404 dokument. Dette gjør man i htaccess filen på følgende måte:

      ErrorDocument 404 /php/404.php

      Nå har vi en error 404 side som er mye mer informativ for både deg og brukeren. På denne måten kan vi også holde oversikt og få hjelp til å finne lenker som er brutt på andres sider som lenker til din side, eller lenker som er brutt på din egen side.

      Som alltid setter jeg pris på tilbakemeldinger.

      Ha en flott jul og et godt nytt år.

      \\ emneord: , , , ,

    1. Dec 04

      Debugging kan være en plagsom ting. Frustrere deg godt og du kan tro all håp er ute. Du kan til slutt tro at det er ingenting feil med kodene dine, for du har gått igjennom alt. Her kommer en liten innføring i hvordan du kan debugge selv. Slik at mange unødvendige spørsmål kommer opp i PHP-forumet på NWF. Listen er satt opp etter hva du burde gjøre kronologisk. Start med punkt én, og jobb deg nedover listen.

      Det er store sjanser for at du løser problemet lenge før du i det hele tatt kommer nedover på listen. De fleste problemer blir løst på punkt 3 av listen.

      1. Les feilmeldingen nøye. Det er en grunn til at feilmeldingene kommer og de er faktisk ganske opplysende. Om det er noe du ikke skjønner i feilmeldingen, som hva T_VARIABLE er, burde du ta et kjapt på T_VARIABLE i google.
      2. Du får oppgitt linjenummer hvor det feiler i en feilmelding. Linjenummeret er veldig essensielt. Du går til linjen og sjekker hva som er galt. Noen ganger når det er PARSE-ERROR lønner det seg å se på linjen før det feilmeldingen sier. Dette kan være tegn som er glemt på linjen ovenfor, som å glemme å avslutte et if-statment, eller du har glemt å avslutte linjen med semikolon.
      3. Har du med MySQL og gjøre vil ikke MySQL feilene komme på lik linje som PHP-feil. Du vil bare ikke få noe resultat ut, og alt kan tyde på at det ikke er innhold i databasen/tabellen din. Er du sikker på at du har innhold med de kriteriene du er ute etter, må du printe ut mysql_error () for å finne ut hvilken feilmelding det er MySQL kommer med. Dette kan du gjøre på mange måter, men den mest brukte er som regel å legge til “or die (mysql_error());” etter siste ) i mysql_query eller tilsvarende som returnerer bool (returnere bool betyr enten sant eller ikke sant. (true|false)). Annen måte du kan gjøre det på er:
        1
        2
        3
        4
        5
        
        $query = mysql_query ("SELECT id FROM table");
        if(!$query) {
           echo "En feilmelding i spørringen:\n";
           echo mysql_error();
        }

        Har du problemer med å løse det som står i feilmeldingen, bruker du manualen til MySQL for å sjekke hva enkelte ting støtter, og hvordan det skal gjøres. Eventuelt tar du å kopierer feilmeldingen inn i google og søker etter lignende saker. Da finner du garantert ut hva som kan løse saken.

      4. Inneholder arrayen/objektet/variabelen det du vil den skal inneholde? Får du helt feil resultat ut på siden din, bruker du var_dump/print_r på objekt/arrays, mens echo/die på variabler, for å sjekke om de inneholder det de skal. Om de ikke gjør det bruker du punkt 5 til å finne hvor det blir feil, og hva det er som virkelig skjer.
      5. Finn ut hvor det feiler! Om du ikke får ut noen feilmelding av noe slag, men du kommer bare ikke frem til dit du vil komme frem betyr det at en if-sjekk slår inn der du ikke vil den skal slå inn. En funksjon returnerer noe feil. Da må du finne ut hvor dette skjer. I hvilken brakke er det nettleseren kommer til? Slik finner du ut hvor det feiler, og dermed hva som er feil. Ta utgangspunkt i der du forventet at du skal komme og sett f.eks die (‘Kom inn hit’); i det ytterste leddet. Kommer den dit? Nei? Da går du inn et nivå, og sjekker om den kommer inn dit. Slik jobber du deg innover for å ekskludere plasser hvor det feiler. Til slutt vil du komme til der den kommer inn. Du finner f.eks ut at det er en funksjon som returnerer false i stedet for true som du ville den skulle gjøre. Er det en selvdeklarert funksjon går du inn på funksjonen for å bruke samme metode for å komme inn til der den faktisk returnerer false.
      6. Siste punkt, er det i alle fall ikke ofte jeg kommer frem til når jeg har brukt metodene ovenfor. Uansett er siste punkt veldig enkelt. Det er nesten garantert at dette punktet vil løse opp problemet ditt også: Just Fucking Google it!

      Eksempel på debug

      Jeg har et nyhetsscript. Når jeg prøver å legge inn informasjon fra form der, vil det aldri bli satt inn noen verdier i tabellen min i databasen. Ingenting kommer. Jeg får ingen feilmelding, men det kommer bare ingenting. Derfor hopper jeg over punkt 1 og punkt 2, siden det ikke er noen feilmelding i PHP. Da kommer jeg rett på punkt 3, som er å se på mysql_error. Jeg legger til som eksemplet ovenfor, og finner at det er for $_POST-verdiene mine ikke får noe som helst slags verdi. Merkelig tenkte jeg. Så jeg går videre til punkt 4, som sier at jeg skal sjekke hva er det $_POST-arrayen inneholder. Så jeg tar en die (print_r($_POST));. Der finner jeg følgende:

      Array (
      [title] => Hei du kåre,
      [message] => Min tekst nedover her
      )

      Da ser jeg va nøkkel-verdiene er på $_POST arrayen. Og finner ut hva jeg gjør feil. Jeg har heletiden brukt $_POST['tittel'] og $_POST['melding'] i stedet for $_POST['title'] og $_POST['message'].

      Slik kan du bruke listen. Og som sagt er det veldig sjeldent at du i det heletatt kommer ned til å måtte google feilen.

      \\ emneord: ,

    2. Dec 04

      Når en skal debugge bruker vi som regel en enkel

      1
      
      die('Her');

      Eller lignende for å indikere hvor det stopper i scriptet. Om du debugger en side som er live, vil du gjerne ikke stoppe hele siden for alle brukere. Derfor vil du gjerne kun vise debugging infoen for deg, og ingen andre. Det er flere måter å gjøre det på, men måten jeg ville ha brukt er funksjonen nedenfor.

      Først og fremst må du opprette en variabel/array som inkluderes til alle sider. Dette skal ikke være et problem om du har en config-fil eller funksjoner som du inkluderer og er nødvendig for systemet. Da er det bare å sette opp følgende en plass hvor hele systemet får tak i det:

      1
      
      $debug_info = array();

      Ikke bare må det settes, men også en funksjon som gjør at det blir enklere å legge til informasjonen i arrayen. Du kan selvfølgelig gjøre det manuelt, men det kan bli litt tungvindt etterhvert. Så vi har en wrapper funksjon som gjør det meste for deg:

      1
      2
      3
      4
      5
      6
      7
      
      function debug ($line, $file, $message, &amp;$debug_info, $statement = true) {
          if($statement) {
              $debug_info[] = 'Line: ' . $line . '. File: ' . $file .'. Message: ' . $message;
              return true;
          }
          return false;
      }

      Her bruker vi noe som kalles call-by-referance. Det vil si at du kan gjøre endringer på variablen satt i argumentet og det vil også skje endringer på variablen du setter i argumentet.

      Måten du finner linje og fil på er enkelt med __line__ og __file__. Dette blir satt i argumenter for å finne hvor det er du kjører funksjonen, og ikke der du oppretter funksjonen.

      $statement er noe du setter til å bli i boolean-verdi. Dette kan være f.eks brukersjekk. Som du kan se eksempel på under her.

      Når du kaller funksjonen kan det ca se slik ut:

      1
      
      debug (__line__, __file__, 'Feiler med MySQL-spørring. Query: ' . $query, $debug_info, ($_SESSION['userid'] == $admin_userid));

      Eller om du ikke har noen kriterier til if-statementet.

      1
      
      debug (__line__, __file__, 'Feiler ved punkt 3.', $debug_info);

      Slik får du informasjon om hvilken fil det er, hvilken linje det skjer på og hva feilen er. Og når du vil skrive ut feilmeldingene kan du enten lage en egen side som inkluderer filen hvor funksjonen og variablen blir satt, eller du kan skrive ut $debug_info nederst på indeks-siden.

      1
      
      print_r($debug_info);

      Slik er det du kan debugge en side live, uten at besøkende ser det som skjer.

      \\ emneord: , ,