Generelle PHP strukturer.

I del 1, fik jeg flyttet data der før eksisterede i MySQL over i Oracle XE installationen. Udgangspunktet er at tage en eksisterende applikation - i dette tilfælde www.oraclekonsulent.dk - flytte denne fra MySQL og køre på vores XE installation. De generelle PHP strukturer, vi nedenfor kigger på vil senere forsøges lagt ind som pakker/procedurer i XE installationen.

I denne del kigger vi på PHP - Det er jo helt op til den enkelte udvikler, hvordan man udvikler. Jeg benytter mig af følgende;

For det første er alle websites jeg beskæftiger mig med lagt lokalt - http://localhost/ - Herunder har jeg opdelt i LOKALESITES, LIVESITES, HISTORIK m.v - under hver enkelt, en www adresse, f.eks vil vores abejdsadresse være http://localhost/LIVESITES/www.oraclekonsulent.dk/

I udgangspunktet har jeg typisk 3 centrale dele i en index.php fil; MAIN.LIB, DBSQL.LIB, STYLE.CSS. Navnene siger næsten sig selv - med sites der har flere end en side - har vi lagt gentagende rutiner ned i MAIN.LIB - et godt eksempel er META tags, header, footer m.v. i en almindelig side.

En typisk start på en index.php side hos mig, ser ud ala;

include_once("lib/dbsql.lib");
include_once("lib/main.lib");

$ORAKONS=new oraclekonsulent;
$DB=new DBSQL;

$DB->DB();

$ORAKONS->printHeader();
$result = $DB->database_query("SELECT * FROM pro_htmltags WHERE page = 'index'");
while ($therow = $DB->database_fetch_row($result)) {
     if (page=='index') {
          print($therow[1]); print($therow[2]);
     } else {
         print($therow[1]); print($therow[2]); echo " "; print($therow[3]); print($therow[4]);
    }
}
$ORAKONS->printMeta();

$ORAKONS->printCSS();

?>

De første statements include_once - inkluderer filerne, main.lib og dbsql.lib - og gør det muligt at arbejde med indholdet; (klasser, funktioner) m.v.

$ORAKONS=new oraclekonsulent;
$DB=new DBSQL;

Jeg arbejder med klasser, når jeg skriver PHP. Og ovenstående angiver blot $ORAKONS samt $DB at være instanser af klasserne DBSQL og oraclekonsulent.

Hvis vi kigger på klassen oraclekonsulent der er inkluderet via filen main.lib

<?php
class oraclekonsulent {
    function printTopBanner() {
 echo <<< end
<table cellpadding="0" cellspacing="0" border="0" height="75" width="940">
 <tr>
  <td class="bagbanner" height="75" width="100%" align="left" valign="top"><h1>Oracle Konsulent&nbsp;&nbsp;<img src="images/fron_oracle.gif" alt="Oracle konsulent" border="0"></h1></td>
 </tr>
 <tr>
  <td class="border" height="1" colspan="2" width="865"></td>
 </tr>
</table>
end;

Så indeholder klassen blot funktionen printTopBanner(). Skulle jeg benytte den funktion i min index.php fil ville jeg skrive;

<BODY link="#000000" vlink="#000000" alink="#000000" class="PageBG">

<?php
 $ORAKONS->printTopBanner();
?>

Altså benytte ORAKONS som jeg har defineret til at være at klassen oraclekonsulent og refererer til -> funktionen printTopBanner() - der som vi ser i main.lib blot laver en ECHO af nogle HTML statements.

Det er helt enkelt, og fordelen for mig som udvikler, er at jeg har printTopBanner liggende et sted - et sted at rette - et sted at finde funktioner osv. Og har jeg flere sider der har en 100% ens "topBanner" - skriver jeg ikke den samme funktion flere gange, men kalder, den ene, hver gang.

main.lib - vil efter vi har flyttet vores data fra MySQL til Oracle XE ikke forandre sig den vil køre uden problemmer - der hvor forandringen vil ske er i DBSQL.LIB

Som navnet indikerer er princippet det samme som ovenstående - et sæt centrale rutiner - men denne gang database rutiner - disse kørte før mod MySQL og skal konveteres til OCI.

Den mest centrale rutine var DB som benyttes til at oprette et link til MySQL og dermed åbne for SELECT, UPDATE,  DELETE m.v vi skal have lavet det samme sæt rutiner til vores applikation.

<?php
Class DBSQL
{
  function DB() {
        $this->host = "localhost";
        $this->db = "oracleblog";
        $this->user = "root";
        $this->pass = "";

       $this->link = mysql_connect($this->host, $this->user, $this->pass);
       mysql_select_db($this->db);
  }

Yderligere havde jeg i min gamle DBSQL.LIB et sæt generelle rutiner til hentning af data;

 function database_query($query) {
     $query = mysql_query($query) or die(mysql_error());
     return $query;
   }

   function database_fetch_array($query) {
    $query = mysql_fetch_array($query);
    return $query;
   }

   function database_fetch_row($query) {
    $query = mysql_fetch_row($query);
    return $query;
   }

Således kunne jeg f.eks gøre således fra min index.php fil - når jeg skulle hente <title> tags el. andet fra databasen dynamisk;

$result = $DB->database_query("SELECT * FROM pro_htmltags WHERE page = 'index'");
while ($therow = $DB->database_fetch_row($result)) {
     if (page=='index') {
          print($therow[1]); print($therow[2]);
     } else {
         print($therow[1]); print($therow[2]); echo "&nbsp;"; print($therow[3]); print($therow[4]);
    }
}

Det er en fuktionalitet jeg gerne vil have med over til vores OCI/PHP miljø; så DBSQL.LIB ser nu omskrevet ud som;

<?php

class DBSQL {
var $ERROR      = "";
    function DB($sid) {
        switch (strtoupper($sid)) {
        case "XE":  return (array("orablog",    "orablog", "XE"));
        }
        
        return(false);
    }

    function database_error($errcode, $errhndl=0) {
        $error = ($errhndl) ? OCIError($errhndl) : OCIError();

        $this->ERROR = trim($error["message"]);

        error_log(sprintf("%s %s %s %s",
            $errcode, $_SERVER["PHP_SELF"], $error["code"], $this->ERROR));
    }

    function database_query($sid, $sql, &$bargs) {
        $this->ERROR = "";

        $dbauth = $this->DB($sid);
        if (empty($dbauth) || ! is_array($dbauth)) {
            $this->ERROR = "Database Error(1).";
            return(false);
        }

        $dbh = @OCILogon($dbauth[0], $dbauth[1], $dbauth[2]);
        if (! $dbh) {
            $this->database_error("OCILogon");
            return (false);
        }

        $stmt = @OCIParse($dbh, $sql);
        if (! $stmt) {
            $this->database_error("OCIParse", $stmt);
            return (false);
        }

        foreach ($bargs as $barg) {
            $$barg[0] = $barg[1];
            @OCIBindByName($stmt, ":$barg[0]", $$barg[0], $barg[2]);
        }

        $rslt = @OCIExecute($stmt);
        if (! $rslt) {
            $this->database_error("OCIExecute(STMT)", $stmt);
            $this->database_error("OCIExecute(RSLT)", $rslt);
            return (false);
        }

        $r_bargs = array();
        foreach ($bargs as $barg) {
            $r_bargs[$barg[0]] = $$barg[0];
        }
        $bargs = $r_bargs;
        
        return ($stmt);
    }

}

Som det ses har jeg valgt blot at omskrive funktionen database_query - den indeholder både connect, parse og execute af et sql udtryk i modsætning fra før. Dvs - hvert SQL udtryk udfører en connect - vi kigger senere på persistant connections. Ligeledes er jeg gået udenom fetch_row og fetch_array. Ligeledes har jeg udvidet med en egentlig begyndende fejl håndterings rutine database_error. Fejl håndterings rutinen skriver til Apache's log fil - et eks. herpå er;

[Mon May 08 09:51:14 2006] [error] [client 192.168.0.100] OCILogon /phporatest.php 12541 ORA-12541: TNS:no listener
[Mon May 08 09:56:10 2006] [error] [client 192.168.0.100] OCILogon /phporatest.php 1017 ORA-01017: invalid username/password; logon denied

Herudover ville det med vore nye rutiner i praksis se således ud;

<?php
include_once("lib/dbsql.lib");
include_once("lib/main.lib");

$ORAKONS=new oraclekonsulent;
$DB=new DBSQL;

$ORAKONS->printHeader();

$sql   = "SELECT * FROM pro_htmltags WHERE page = 'index'";

$bargs = array();
array_push($bargs, array("IN_LOGIN", 'orablog', -1));

$stmt = $DB->database_query("XE", $sql, $bargs); if (! $stmt) return(false);

$nrows = @OCIfetchstatement($stmt, $results);

for ($i = 0; $i < $nrows; $i++ )
{
    echo $results["TITLE_TAG_START"][$i];
    echo $results["TITLE_TAG_DYN"][$i];
    echo $results["TITLE_TAG_END"][$i];
}

$ORAKONS->printMeta();

$ORAKONS->printCSS();


echo "<body>";
echo "<table border=1 cellspacing='0' width='50%'>";
echo "<tr>";
echo "<td colspan='2'>";
echo "<div class='header'>Data fra HR skemaet i din XE database</div><br>";
echo "</td>";
echo "</tr>";
echo "<tr>";
echo "<td><b>Navn</b></td><td><b>L&oslash;n</b></td></tr>";

$bgcolor=0;

$sql   = "select first_name, salary from hr.employees";

$bargs = array();
array_push($bargs, array("IN_LOGIN", 'orablog', -1));

$stmt = $DB->database_query("XE", $sql, $bargs); if (! $stmt) return(false);

$nrows = @OCIfetchstatement($stmt, $results);

for ($i = 0; $i < $nrows; $i++ )
{
    if ($bgcolor==0) {
              echo "<tr bgcolor='#E4E4E4'>";
             $bgcolor=1;
    } else {
              echo "<tr bgcolor='#CECECE'>";
              $bgcolor=0;
    }
    echo "<td>" . $results["FIRST_NAME"][$i] . "</td>";
    echo "<td>Kr " . number_format($results["SALARY"][$i],   2). "</td>";
    echo "</tr>";
   }

echo "<tr><td colspan='2'> Antal fundne poster: $nrows</td></tr></table>";

?>

Det giver et forventet output nedenfor;

 

Den opmærksomme ser straks at udtrykket;

$stmt = $DB->database_query("XE", $sql, $bargs); if (! $stmt) return(false);

Er der magien ligger. Det betyder vi kan koncerentrere os om blot at huske $DB->database_query udtrykket i vores PHP filer - og lidt glemme OCI_conn, parse osv. idet vi holder det til den centrale DBSQL fil.

I princippet kunne ovenstående eksemple godt være oraclekonsulent.dk - det er helt samme fremgangsmåde - et sæt generelle standard rutiner til at skrive gentagene data, defineret et sted. Ligeledes med Database funktioner - al hentning af data - gøres vha. et sæt generelt definerede database rutiner. Udseende har vi defineret via stylesheets. og disse defineres hos mig via rutinen printCSS() defineret i main.lib.

function printCSS() {
    echo <<< end
<link rel="stylesheet" href="css/portalStyle.css" type="text/css" title="ALMINDELIG" media="screen">
</head>
end;
}

Næste del indeholder, hvordan vi tager de genrelle rutiner (main.lib) - får lagt dem ned i XE instansen som pakker/procedurer - og ligeledes hvordan vi udvider DBSQL.LIB - dvs jeg vil dels kigge på persistant connections men også at hver php side i princippet bør se om en forbindelse er tabt - og i sådanne tilfælde kunne se det og lave rollback, frigøre låsninger m.v.

Yderligere informationer

PHP: OCI funktioner

Oracle: PHP Developer center

Oracle Konsulenter - PHP artikler