6. CRUD selfjoin table forum – message board PDO SQLite

HOME   . Downloads

Excellent code to learn object oriented programming, model-view-controller code separation (structured programming), single page application (4 utility-helper functions + 5 CRUD functions), all one selfjoin table CRUD code in one script, require scripts, not URL them, most code outside Apache doc root, Javascript msg & yes-no dialogs and key TAB->ENTER.
(see Home -> site_ver2.rar).

6.Dec.2015 code refactored and parialy improved. Later (next year) I shall finish all I planed and upload also  site_ver2.rar where is code outside appl. root. Below is newest code, other is in
Home -> site_ver2.rar

2.Sept.2015 Download – see site_ver2.rar from my article 9. Code below is enough to understand – it is not much changed.

1. Download sqlitestudio and install DDL at end tema.php

2.     1_sync_tema_sifrarnik_JtoH.bat

rem SINCHRONIZATION: 2 click this .bat script (or Git)
rem J:\awww\apl\dev1\01apl\04tema\1_sync_tema_sifrarnik_JtoH.bat
rem
rem robocopy <Original Folder> <Destination Folder> /e /purge
rem **** OUTPUTS EG : *****
rem Total Copied Skipped Mismatch FAILED Extras
rem Dirs : 20 0 20 0 0 0
rem Files : 94 6 88 0 0 0
rem Bytes : 453.9 k 36.6 k 417.2 k 0 0 0
rem Times : 0:00:00 0:00:00 0:00:00 0:00:00
rem
rem Speed : 708811 Bytes/sec. 40.558 MegaBytes/min.
rem Ended : 6. prosinca 2015. 20:33:19

rem 1. resources outside appl. tree - utils, settings, css, img
rem 1.1 
robocopy J:\awww\apl\dev1\config_site.php H:\awww\apl\dev1\config_site.php /e /purge
rem 1.2
robocopy J:\awww\apl\zinc H:\awww\apl\zinc /e /purge

rem 2. inside appl. tree
robocopy J:\awww\apl\dev1\01apl\04tema H:\awww\apl\dev1\01apl\04tema /e /purge

pause

3. tema.php Code – all in one script ~350 lines :

<?php
/**
* Themes (threads) (msg-reply, task, menu) 
* Teme (niti) (poruka-odgovor, zadaća, izbornik)
* PHP, Javascript, PDO SQLite3, selfjoin, 2015.12.7
*
* LICENCE: Free code example - if you use it, do not remove this:
* Slavko Srakočić, Croatia, Zagreb
* see my blog http://phporacle.altervista.org
*
*
* 1. ADRESSES :
* J:\awww\apl\dev1\01apl\04tema\tema.php
* http://dev1:8083/01apl/04tema/tema.php
*
* 2. robocopy (or Git) SYNCHRONIZATION: 2 click this .bat script (or git)
* J:\awww\apl\dev1\01apl\04tema\1_sync_tema_sifrarnik_JtoH.bat
*
* 3. DDL: see CREATE TABLE message at this script end
*
* 4. Home page looks like :
* Open new thema (nit, thread) Help = Klik Ord.Nr
* |...| = |bytes| idniti, rbruniti, nivo, id, idviši |
* 1. MSGs self join sqlite3 THIS PG by ss @ 2015-03-25 01:41 |DELETE| 1900 |UPDATE |1,0,0 ,1,0|
*     1. funkcije by ss @ 2015-03-25 01:42 |DEL| 1654 |UPD|1,1,1 ,2,1|
*          2. Re: funkcije by ss @ 2015-03-26 14:31 |DEL| 14 |UPD|1,2,2 ,18,2|
*     3. funkcija save() by ss @ 2015-03-25 01:52 |DEL| 1296 |UPD|1,3,2 ,3,2|
* ...
* 2. Z-WAMP menu by aa @ 2015-12-05 15:58 |DELE| 14 |UPD |3,0,0 ,25,0|
*
*
* 5. PHP CREATED JAVASCRIPT MSG & YES-NO DIALOGS :
* $this->utl->phpjsmsg(7 parameters), phpjsmsgyn() 
* 6. F U N C T I O N S IN THIS SCRIPT
*LINE FUNCTION WHAT (HOW) IS DOING 
*89: public __construct() { // INITIALIZE (B O O T S T R A P I N G)
*151: public ctr() { // M A N A G E (R O U T I N G)
*192: protected crerec() { // crud1. FRONTEND A D D SELFJOIN ROW
*257: protected tbl() { // crud2. D I S P L A Y HIERARCHY
*363: protected frm_rpt() { // crud3. R O W REPORT (m s g b o d y...)
*417: protected frm_post() // crud4. ENTER USER DATA & PROCESS IT
*491: protected delrec() { // crud5 BACKEND D E L E T E R O W
*522: protected valid() {
*538: protected logAndDie(Exception $e) {
*
*/
use utlmoj\utlmoj as utl;

ini_set('max_execution_time', 0);
$start = microtime(true);

$selfj = new SelfJoin(); // should be named: MessageBoard, MainMenu...
$selfj->ctr(); //ROUTING = starts scripts according url params cmd, ... 

$end = microtime(true);

echo '<br />'.'Vrijeme izvođenja: '.($end - $start) . ' sekundi';

class SelfJoin { // should be named: MessageBoard, MainMenu...
 
 // 1. C R U D (M O D E L) PROPERTIES :
 protected $inTransaction = false;
 
 protected $db;
 protected $table;
 protected $idname;
 protected $idval;
 // c r u d actions (CURSORS = named sql set process comands):
 // D = cursor for deleting
 protected $cdel_row_byid ; 
 // R e a d cursor
 protected $cr_max_rbrdet_inm ;
 protected $cr_byid_nivo_orddet_inm ;
 protected $cr_byid ; // R E A D r o w B Y I D (M O D E L c R u d)
 protected $cr_byid_colname ; 
 // U p d a t e cursor
 protected $cu_incr_rbrdet_inm ;
 // C r e a t e cursor
 protected $cc_mast_or_det ;
 
 // 2. U T L S F U N C T I O N S (H E L P E R S) :
 protected $utl; // OUTSIDE WEBSERVERDOCROOT
 // a d r e s s properties :
 protected $curpgurl;
 protected $curpgpath;

 // 3. V I E W PROPERTIES :
 protected $form_errors = array();
 
 
 public function __construct() { // I N I T I A L I Z E (B O O T S T R A P I N G)
 set_exception_handler(array($this,'logAndDie'));
 
 // C R U D (m o d e l) properties :
 $this->table = 'message';
 $this->idname = 'id';
 $this->idval_url = isset($_REQUEST[$this->idname]) ?
 intval($_REQUEST[$this->idname]) : '';
 
 // C R U D actions (cursors, named sql set process comands):
$this->cdel_row_byid = // cd_ means cursor for deleting
"DELETE FROM $this->table WHERE $this->idname=?"; //$values[]=$parent_id;

$this->cr_max_rbrdet_inm;
"SELECT MAX(thread_pos) FROM $this->table WHERE thread_id = ? AND parent_id = ?"; 

$this->cr_byid_nivo_orddet_inm= // cr_ means cursor for reading
"SELECT thread_id,level,thread_pos FROM $this->table WHERE $this->idname=?";

$this->cu_incr_rbrdet_inm=
"UPDATE $this->table SET thread_pos = thread_pos + 1 WHERE thread_id = ? AND thread_pos >= ?";

$this->cc_mast_or_det="INSERT INTO $this->table "
. "($this->idname, thread_id,parent_id, thread_pos, posted_on, level, author, subject, body) "
.'VALUES (?,?,?,?,?,?,?,?,?)';

$this->cr_byid=
"SELECT author,subject,body,posted_on FROM $this->table WHERE $this->idname = ?";

$this->cr_byid_colname=
"SELECT subject FROM $this->table WHERE $this->idname = ?";

 
 // 1. g l o b a l u t l s, s e t t i n g s :
 $curpgpath =__FILE__; $mastpgpath=$curpgpath; //dirname(__DIR__) ;
 $this->curpgpath = $curpgpath;
 require_once($_SERVER['DOCUMENT_ROOT'].'/config_site.php');
 $this->curpgurl = $curpgurl ; 
 //htmlentities($_SERVER['PHP_SELF'],ENT_QUOTES);
 //$_SERVER['DOCUMENT_ROOT'] = eg J:\awww\apl\dev1\
 
 $this->utl = $utl;
 
?><SCRIPT LANGUAGE="JavaScript"><!-- Begin 
<?php echo file_get_contents($jsd.$ds.'key_pressed.js');?>//End --></SCRIPT><?php if ('') $utl->phpjsmsg('aaaaaaaaaa key_ pressed.js IS INCLUD E D');
 
 // 2. g l o b a l c r u d :
 $dbi = 'sqlite3'; $sqlitetbl = 'tema.sqlite3' ; //$dsn='default';
 require_once($gloresdir.$ds.'db_conn.php'); //requires klase/dbi.php
 $this->db = $db;
// ******************************************
 $title = '<h1>Teme (threads) (poruka-odgovor, zadaća, izbornik) selfjoin PHP PDO SQLite3</h1>' ;
 $title2 = 'Teme selfjoin'; // ibrowser tab txt
 $basecss=$cssd.$ds.'style00.css'; //'default' or $cssd.$ds.'style00.css';
 include ($gloresdir.'/hdr.php');
 //include ($cnfgd.$ds.'hdr.php');

 } // e n d _ _ c o n s t r u c t
 
 
 
 
 public function ctr() { // M A N A G E (R O U T I N G)
 // The value of $_REQUEST['cmd'] tells us what to do
 $cmd = isset($_REQUEST['cmd']) ? $_REQUEST['cmd'] : 'tbl';
 // frontend - user actions (backend in sqlitestudio) :
 switch ($cmd) {
 case 'delrec':
 $this->utl->phpjsmsgyn('Obrisati redak ?'
 , '?cmd=delrecyes&'.$this->idname.'='.$this->idval_url);
 break;
 case 'delrecyes': 
 //case 'delrec': 
 if ($this->idval_url) $this->delrec($this->idval_url);
 else $this->utl->phpjsmsg('Nije zadana šifra retka za brisanje !');
 $this->tbl();
 break;
 case 'frm_rpt': // read an self join r o w
 $this->frm_rpt();
 break;
 case 'frm_post': // display form to post self join r o w
 $this->frm_post();
 break;
 case 'crerec': // insert - i n s e r t posted self join r o w
 if ($this->valid()) { // if m s g e is v a l i d,
 $this->crerec(); // then i n s e r t it
 $this->tbl(); // and display self join list
 } else {
 $this->frm_post(); // otherwise, redisplay the posting form
 }
 break;
 case 'tbl': // show self join r o w s list
 default:
 $this->tbl();
 break;
 }
 }





 // F R O N T E N D - user actions :
 protected function crerec() { // crud1. FRONTEND A D D SELFJOIN ROW
 $parent_id = isset($_REQUEST['parent_id']) ?
 intval($_REQUEST['parent_id']) : 0;
 // Make sure m s g e doesn't change while we're working with it.
 $this->db->beginTransaction();
 $this->inTransaction = true;
 // is this m s g e a reply?
 if ($parent_id) {
 // get the thread, level, and thread_pos of the parent m s g e
 //"SELECT thread_id,level,thread_pos FROM $this->table WHERE $this->idname=?";
 $st = $this->db->prepare($this->cr_byid_nivo_orddet_inm);
 $st->execute(array($parent_id));
 $parent = $st->fetch();
 // a reply's level is one greater than its parent's
 $level = $parent['level'] + 1;
 // what's biggest thread_pos in this thread among m s g e s with the same parent? 
 //"SELECT MAX(thread_pos) FROM $this->table WHERE thread_id = ? AND parent_id = ?";
 $st = $this->db->prepare($this->cr_max_rbrdet_inm);
 $st->execute(array($parent['thread_id'], $parent_id));
 $thread_pos = $st->fetchColumn(0);
 // are there existing replies to this parent?
 if ($thread_pos) {
 // this thread_pos goes after the biggest existing one
 $thread_pos++;
 } else {
 // this is the first reply, so put it right after the parent
 $thread_pos = $parent['thread_pos'] + 1;
 }
 // increment thread_pos of all m s g e s in the thread that come after this one 
 //"UPDATE $this->table SET thread_pos = thread_pos + 1 WHERE thread_id = ? AND thread_pos >= ?";
 $st = $this->db->prepare($this->cu_incr_rbrdet_inm);
 $st->execute(array($parent['thread_id'], $thread_pos));
 // the new m s g e should be i n s. with the parent's thread_id
 $thread_id = $parent['thread_id'];
 } else {
 // m s g e is not reply, so it's the s t a r t of new t h r e a d
 $thread_id = $this->db->query(
 "SELECT MAX(thread_id) + 1 
 FROM $this->table")->fetchColumn(0);
 // If there are no rows yet, make sure we s t a r t at 1 for thread_id
 if (! $thread_id) {
 $thread_id = 1;
 }
 $level = 0;
 $thread_pos = 0;
 }
 // i n s e r t m s g e into DB. Using prepare() and execute() makes sure that all fields are properly quoted 
 //"INSERT INTO $this->table "
 //. "($this->idname, thread_id,parent_id, thread_pos, posted_on, level, author, subject, body) "
 //.'VALUES (?,?,?,?,?,?,?,?,?)';
 $st = $this->db->prepare($this->cc_mast_or_det);
 $st->execute(array(
null, $thread_id, $parent_id, $thread_pos, date('c'), $level
,$_REQUEST['author'], $_REQUEST['subject'],$_REQUEST['body']));
 // Commit all the operations
 $this->db->commit();
 $this->inTransaction = false;
 }
 
 
 
 
 
 
 
 protected function tbl() // crud2. D I S P L A Y H I E R A R C H Y
 { //print '<h2><a href="http://dev:8083/test/books/a01cookbook/tema.php">Teme (poruka-odgovor)</a></h2>';
 // provide a way to p o s t non-reply m s g e
 // IZBORNIK t b l - i c e :
 
 // t b l h d r r o w (action menu) :
 $lnk_addmaster = '<strong>'."<a class='btn' href='".$this->curpgurl
 ."?cmd=frm_post'>Otvoriti novu temu (nit, thread)</a>"
 .'</strong> ';
 print $lnk_addmaster;
 if ('1') print '&nbsp;&nbsp;&nbsp; Help = Klik Rbr.
 &nbsp;&nbsp;&nbsp;
 |...| = | bytes|idniti, rbruniti, nivo, id, idviši| '
 .'<hr/>';

 //order m s g s by their thread (thread_id) and their position within thread (thread_pos)
 $st = $this->db->query(
 "SELECT $this->idname, subject,author"
 .",LENGTH(body) body_length,posted_on,level,thread_id,thread_pos
 ,parent_id,url"
 ." FROM $this->table ORDER BY thread_id,thread_pos");
 
 
 while ($row = $st->fetch()) {
 
 $when = date('Y-m-d H:i', strtotime($row['posted_on']));
 
 // indent m s g e s with level > 0
 print '<font style="font-family: Courier;">'
 .str_repeat('&nbsp;',2 * $row['level']) .'</font>';

 // print info about m s g with
 // - link to open page eg http://dev1:8083/01apl/04tema/tema.php
 // - link to read it
 print
 // ----------- 1. thread_id (NIT) = ORD.NO :
 ( ($row['level'] == 0) // branch root
 ?
 //'<strong>'
 // Help (msg content report) = Klik Rbr :
 "<a href='" . $this->curpgurl
 . "?cmd=frm_rpt&amp;$this->idname={$row[$this->idname]}"
 ."'>"
 . '<span class="btnsmall">'
 .'<font style="color: black; font-family: Courier;">' 
 // background-color: red; 
 .str_repeat('&nbsp;', (6 - strlen($row['thread_id'])) )
 .$row['thread_id'] //.'</strong>'
 .'</font>'
 . '</span>'
 .'</a>'
 .'. ' 
 :
 // ----------- 2. thread_pos = ORD.NO IN THREAD - Help (msg content report) = Klik Rbr u niti :
 "<a href='" . $this->curpgurl
 . "?cmd=frm_rpt&amp;$this->idname={$row[$this->idname]}"
 ."'>"
 //.'&nbsp;&nbsp;'.$row['thread_pos'].'. ' .'</strong>'
 . '<span>'
 .'<font style="color: black; background-color: white; font-family: Courier;">' 
 .str_repeat('&nbsp;', (6 - strlen($row['thread_pos'])) )
 .$row['thread_pos'] //.'</strong>'
 .'</font>'
 . '</span>'
 .'</a>'
 .'. ' 
 
 
 )
 // Klik msg to open page :
 // eg http://dev1:8083/index.php?cmd=lsweb&dir=J:\awww\apl\dev1\test\01info
 ."<a href='" . htmlentities($row['url'],ENT_QUOTES)
 ."'>"
 . htmlentities($row['subject'],ENT_QUOTES)
 .'</strong>'
 . '</a> '
 . ' by '. htmlentities($row['author'],ENT_QUOTES) . ' @ '
 . htmlentities($when,ENT_QUOTES)
 ;
 // L I N K c m d = d e l r e c
 printf('<a href="%s?cmd=delrec&'.$this->idname.'=%s'
 //.'&'.'rrgo'.'=%s'
 .'">'.'%s</a>'
 , $this->curpgurl
 , $row[$this->idname]
 //, $pgrr1
 , '<font style="color: red;">'.' |'.'</font>'
 .'BRIŠI' // d e l e t e
 .'<font style="color: red;">'.'|'.'</font>'
 );
 print " {$row['body_length']} " // bytes|
 . " |PROMJ " // u p d a t e
 ;
if ('1') print "|{$row['thread_id']},{$row['thread_pos']},{$row['level']}
,{$row[$this->idname]},{$row['parent_id']}|";
print '<br/>';

 } // e n d p r i n t r o w s
 
 print '<hr/>'.$lnk_addmaster;
 } // e n d f n t b l ( )

 
 
 
 
 protected function frm_rpt() { // crud3. R O W REPORT (m s g b o d y...)
 // make sure the m s g e i d we are passed is an integer and really represents a m s g e 
 if (! isset($_REQUEST[$this->idname])) {
 throw new Exception('Nije formirana šifra poruke');
 }
 $id = intval($_REQUEST[$this->idname]);
 //"SELECT author,subject,body,posted_on FROM $this->table WHERE $this->idname = ?";
 $st = $this->db->prepare($this->cr_byid);
 $st->execute(array($id));
 $msg = $st->fetch();
 if (! $msg) {
 throw new Exception('Loša šifra poruke');
 }
 /* don't display user-entered HTML, but display newlines as
 HTML line breaks */
 $body = str_replace('{{/strong}}','</strong>',
 str_replace('{{strong}}','<strong>',
 nl2br(htmlentities($msg['body']))
 ));
 //
 while (false !== strpos($body, '{{url}}')) {
 $beg = strpos($body, '{{url}}');
 $end = strpos($body, '{{/url}}');
 $url = substr($body, $beg+7, $end - $beg -7);
 $url2 = '<a href="'.$url.'">'.$url.'</a>'; // </strong>
 $body = str_replace('{{url}}'.$url.'{{/url}}',$url2,$body);
 //$body .= '<br />'.$url2;
 }
 //
 // display m s g e with links to reply and return to the m s g e list
 $subject = htmlentities($msg['subject']);
 $author = htmlentities($msg['author']);
 // ---------------------------------------
 // h d r m e n u f r m _ v i e w - a
 // --------------------------------------
 print<<<_HTML_
 <h2>$subject</h2>
 <h3>
 by $author &nbsp; &nbsp;
 <a href="$this->curpgurl?cmd=frm_post&parent_id=$id">Odgovor</a>
 &nbsp; &nbsp; <a href="$this->curpgurl?cmd=tbl">Stablo poruka</a>
 </h3>
 <hr/>

 <p>$body</p>

_HTML_;
 } // tbl()

 
 
 
 
 
 protected function frm_post() // crud4. ENTER USER DATA & PROCESS IT
 {
 $safe = array();
 foreach (array('author','subject','body') as $field) {

 // escape input values :
 if (isset($_POST[$field])) {
 $safe[$field] = htmlentities($_POST[$field]);
 } else { $safe[$field] = ''; }

 // make err m s g s display in red :
 if (isset($this->form_errors[$field])) {
 $this->form_errors[$field] = '<span style="color: red">' .
 $this->form_errors[$field] . '</span><br/>';
 } else { $this->form_errors[$field] = ''; }
 } // e n d f o r e a c h


 // is this m s g e reply ?
 if (isset($_REQUEST['parent_id']) &&
 $parent_id = intval($_REQUEST['parent_id'])) {
 // send parent_id along when form is submitted
 $parent_field = sprintf(
 '<input type="hidden" 
 name="parent_id" 
 value="$this->idname" />'
 , $parent_id);
 // if no subject's been passed in, use parent's subject
 if (! strlen($safe['subject'])) {
 //"SELECT subject FROM $this->table WHERE $this->idname = ?";
 $st = $this->db->prepare($this->cr_byid_colname);
 $st->execute(array($parent_id));
 $parent_subject = $st->fetchColumn(0);
 /* prefix 'Re: ' to parent subject if it exists and
 doesn't already have 'Re:' */
 $safe['subject'] = htmlentities($parent_subject);
 if ( $parent_subject
 && (! preg_match('/^re:/i',$parent_subject)))
 { $safe['subject'] = "Re: {$safe['subject']}"; }
 }
 } else { $parent_field = ''; }


 // display posting form, with errors and default values
 print<<<_HTML_
 <form method="post" action="$this->curpgurl">
 <table>
 <tr>
 <td>Your Name:</td>
 <td>{$this->form_errors['author']}
 <input type="text" name="author" value="{$safe['author']}" />
 </td>
 <tr>
 <td>Subject:</td>
 <td>{$this->form_errors['subject']}
 <input type="text" name="subject" value="{$safe['subject']}" />
 </td>
 <tr>
 <td>Poruka:</td>
 <td>{$this->form_errors['body']}
 <textarea rows="4" cols="30" wrap="physical"
 name="body">{$safe['body']}</textarea>
 </td>
 <tr><td colspan="2"><input type="submit" value="Pošaljite poruku" /></td></tr>
 </table>
 $parent_field
 <input type="hidden" name="cmd" value="crerec" />
 </form>
_HTML_;
}



 // B A C K E N D - administrator actions :
 protected function delrec($parent_id) { // crud5 BACKEND D ELETE R OW
 //$parent_id = isset($_REQUEST[$this->idname]) ? intval($_REQUEST[$this->idname]) : 0;
// $this->idval_url
//basename(__FILE__).' SAYS'.'<br>'.'$id'.'=='.$parent_id.'=='
if ('') $this->utl->phpjsmsg('***** '.__FUNCTION__.'() SAYS: ' 
 .'<br>'.'$dml=***'.$this->cdel_row_byid.'***<br>'.'?=$parent_id=***'
 .$parent_id.'***');
 // is this m s g e a reply?
 if ($parent_id) {
 // Make sure m s g e doesn't change while we're working with it.
 $this->db->beginTransaction();
 $this->inTransaction = true;
 //"DELETE FROM $this->table WHERE $this->idname=?"; //$values[]=$parent_id;
 $st = $this->db->prepare($this->cdel_row_byid);
 //or $this->db->get_con()->prepare ?
 $st->execute(array($parent_id));
 // fetchAll() is needed only for s e l e c t 
 //Commit all the operations
 $this->db->commit();
 $this->inTransaction = false;
 } 
 }

 
 
 
 



 // 5. makes sure something is entered in each field :
 protected function valid() {
 $this->form_errors = array();
 // R E Q U I R E D U S E R D A T A :
 if (! (isset($_POST['author']) && strlen(trim($_POST['author'])))) {
 $this->form_errors['author'] = 'Upišite vaše ime (autor).';
 }
 if (! (isset($_POST['subject']) && strlen(trim($_POST['subject'])))) {
 $this->form_errors['subject'] = 'Upišite naslov poruke.';
 }
 if (! (isset($_POST['body']) && strlen(trim($_POST['body'])))) {
 $this->form_errors['body'] = 'Upišite tekst poruke.';
 }
 return (count($this->form_errors) == 0);
 }

 // 6.
 protected function logAndDie(Exception $e) {
 print 'ERROR: ' . htmlentities($e->getMessage());
 if ($this->db && $this->db->inTransaction()) {
 $this->db->rollback();
 }
 exit();
 }
 
 
 
 
 
} // e n d c l a s s



// <strong><a href="http://...">http://...</a></strong>
// if (false !== strpos($string, $substring)) { /* found it! */ }
// if (strpos($haystack, $needle) !== false) echo 'match!';
// $withoutCommas = is_numeric(str_replace(',', '', $number));

/*
ALTER TABLE message RENAME TO sqlitestudio_temp_table;

CREATE TABLE message (
 id INTEGER PRIMARY KEY AUTOINCREMENT,
 subject CHAR (255),
 url VARCHAR (500),
 thread_id [INT UNSIGNED] NOT NULL,
 thread_pos [INT UNSIGNED] NOT NULL,
 level [INT UNSIGNED] NOT NULL,
 parent_id [INT UNSIGNED] NOT NULL,
 author CHAR (255),
 body MEDIUMTEXT,
 posted_on DATETIME NOT NULL
);

INSERT INTO message (
id
,subject
,url
,thread_id
,thread_pos
,level
,parent_id
,author
,body
,posted_on
)
SELECT
id
,subject
,url
,thread_id
,thread_pos
,level
,parent_id
,author
,body
,posted_on
FROM sqlitestudio_temp_table;

DROP TABLE sqlitestudio_temp_table;




 TO INCLUDE RESULTS OF EXECUTING FN OR EXPRESSION WITHIN A STRING
 You can put vars, obj.prop, array el. (if subscript is unquoted) directly in double-quoted strings:

 eg print "You owe $amounts[payment] immediately.";
 eg print "My circle's diameter is $circle->diameter inches.";

 Curly braces around more complicated expressions to interpolate them into a string:

 print "I have {$children} children.";
 print "You owe {$amounts['payment']} immediately.";
 print "My circle's diameter is {$circle->getDiameter()} inches.";

 Direct interpolation or string concat. also works with heredocs:
 print <<< END
 Right now, the time is
 END
 . strftime('%c') . <<< END
 but tomorrow it will be
END
 . strftime('%c',time() + 86400);

 
 {{url}} http://dev1:8083/my_dev/pdo/tema/tema.php {{/url}}
 J:\awww\apl\dev1\my_dev\pdo\tema\tema.php
 J:\dev_web\htdocs\test\books\a01cookbook\tema.php


Promjene podataka programom J:\aplp\aplp\sqlitestudio\SQLiteStudio.exe

SELFJOIN TABLE :
 1. C INSERT frm data,
 2. R DISPLAY tbl, row,
 3. V VALIDATE, E set_exception_handler
Not neccessarily here:
U UPDATE and D DELETE WITH J:\aplp\aplp\sqlitestudio\SQLiteStudio.exe

*/

5. CRUD simple table (ID,…some data) PDO SQLite

HOME  2.Sept.2015 Download – see site_ver2.rar from my article 9.

Excellent code to learn object oriented programming, model-view-controller code separation (structured programming), single page application (require scripts, not URL them), most code outside Apache doc root.

1. Download sqlitestudio and install DDL below.

--*****************************************
-- sqlite 3
--*****************************************
/*
SELECT * FROM message ;
select * from zodiac ;
*/

/*
http://dev:8083/test/books/a01cookbook/tema.php
J:\dev_web\htdocs\test\books\a01cookbook\tema.php

sqlite db is: J:\dev_web\htdocs\test\books\a01cookbook\tema.sqlite
                                         
SELFJOIN TABLE :
   1. C INSERT frm data, 
   2. R DISPLAY tbl, row, 
   3. V VALIDATE, E set_exception_handler 
Not neccessarily here:
U UPDATE and D DELETE WITH J:\aplp\aplp\sqlitestudio\SQLiteStudio.exe

TEME (msgs-PORUKE I replays-ODGOVORI) SELFJOIN
TEME (msgs-PORUKE I replays-ODGOVORI) SELFJOIN
----------------------------------------------------------------
5 KEYS: id,thread_id,parent_id,level,thread_pos
----------------------------------------------------------------
1 1 0 0 0   TEMA1 (thread1) by ss @ 2015-03-25 00:41 (99 bytes)
2 1 1 1 1     funkcije by ss @ 2015-03-25 00:42 (242 bytes)
3 1 2 2 2       funkcija save() by ss @ 2015-03-25 00:52 (1335 bytes)
6 1 2 2 3       funkcija frm_post() by ss @ 2015-03-25 19:29 (303 bytes) 

4 2 0 0 0   TEMA2 CRUD šifrarnika sqlite3 by ss ...

5 3 0 0 0   TEMA3 MAPE web servera by ss ...
______________________________________________________________
Otvoriti novu temu (nit, thread)


*/

-- sqlite db: J:\dev_web\htdocs\test\books\a01cookbook\tema.sqlite
CREATE TABLE message (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  posted_on   DATETIME NOT NULL,
  author      CHAR(255),
  subject     CHAR(255),
  body        MEDIUMTEXT,
  thread_id   INT UNSIGNED NOT NULL,
  parent_id   INT UNSIGNED NOT NULL,
  level INT   UNSIGNED     NOT NULL,
  thread_pos  INT UNSIGNED NOT NULL
);



--sqlite db: J:\dev_web\htdocs\test\books\a01cookbook\zodiac.sqlite
CREATE TABLE zodiac (
  id          INT UNSIGNED NOT NULL,
  sign        CHAR(11),
  symbol      CHAR(13),
  planet      CHAR(7),
  element     CHAR(5),
  start_month TINYINT,
  start_day   TINYINT,
  end_month   TINYINT,
  end_day     TINYINT,
  PRIMARY KEY(id)
)
;

INSERT INTO zodiac VALUES (1,'Aries','Ram','Mars','fire',3,21,4,19);
INSERT INTO zodiac VALUES (2,'Taurus','Bull','Venus','earth',4,20,5,20);
INSERT INTO zodiac VALUES (3,'Gemini','Twins','Mercury','air',5,21,6,21);
INSERT INTO zodiac VALUES (4,'Cancer','Crab','Moon','water',6,22,7,22);
INSERT INTO zodiac VALUES (5,'Leo','Lion','Sun','fire',7,23,8,22);
INSERT INTO zodiac VALUES (6,'Virgo','Virgin','Mercury','earth',8,23,9,22);
INSERT INTO zodiac VALUES (7,'Libra','Scales','Venus','air',9,23,10,23);
INSERT INTO zodiac VALUES (8,'Scorpio','Scorpion','Mars','water',10,24,11,21);
INSERT INTO zodiac VALUES (9,'Sagittarius','Archer','Jupiter','fire',11,22,12,21);
INSERT INTO zodiac VALUES (10,'Capricorn','Goat','Saturn','earth',12,22,1,19);
INSERT INTO zodiac VALUES (11,'Aquarius','Water Carrier','Uranus','air',1,20,2,18);
INSERT INTO zodiac VALUES (12,'Pisces','Fishes','Neptune','water',2,19,3,20);

2. Controller and view ~ 120 lines :

<?php
// http://dev:8083/test/books/a01cookbook/
// J:\dev_web\htdocs\test\books\a01cookbook\zodiac.php

    // ***********************************
    // 1. M O D E L - file where b u i l d_ q u e r y ( )  is defined
    // ***********************************
$fields = array(
   'sign'
 , 'symbol'
 , 'planet'
 , 'element'
 , 'start_month'
 , 'start_day'
 , 'end_month'
 , 'end_day'
);
$lbls = array(
    'Znak'
  , 'Simbol'
  , 'Planeta'
  , 'Element'
  , 'Od mjeseca'
  , 'Od dana'
  , 'Do mjeseca'
  , 'Do dana'
);

include __DIR__ . '/mdl.php';
$db = new PDO('sqlite:zodiac.sqlite');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);


    // ***********************************
    // 2. C O N T R O L L E R
    // ***********************************
$cmd = isset($_REQUEST['cmd']) ? $_REQUEST['cmd'] : 'show';
switch ($cmd) 
{
   case 'edit':
       try {
          $st = $db->prepare('SELECT ' . implode(',',$fields) .
                             ' FROM zodiac WHERE id = ?');
          $st->execute(array($_GET['id']));
          $row = $st->fetch(PDO::FETCH_ASSOC);
       } catch (Exception $e) {
           $row = array();
       }
   case 'add':
           print '<form method="post" action="' .
                 htmlentities($_SERVER['PHP_SELF']) . '">';
           print '<input type="hidden" name="cmd" value="save">';
           
           print '<table>';
           
           if ('edit' == $cmd) {
               printf('<input type="hidden" name="id" value="%d">',
                      $_GET['id']);
           }
           foreach ($fields as $field) {
               if ('edit' == $cmd) {
                  $value = htmlentities($row[$field]);
               } else {
                   $value = '';
               }
               printf('<tr><td>%s: </td><td><input type="text" name="%s" value="%s">',
                      $field, $field, $value);
               printf('</td></tr>');
           }
           print '<tr>
                <td></td>
                <td><input type="submit" value="Save"></td>
              </tr>';
           
           print '</table></form>';
           break;
   case 'save':
           try {
             $st = build_query($db,'id',$fields,'zodiac');
             print 'Added info.';
           } catch (Exception $e) {
             print "Couldn't add info: " . htmlentities($e->getMessage());
           }
           print '<hr>';
   case 'show':
          // ***********************************
          // 3. V I E W - P A G I N A T O R
          // ***********************************
       default:
           $self = htmlentities($_SERVER['PHP_SELF']);
           
           /* without P A G I N A T O R :
           foreach ($db->query('SELECT id,sign FROM zodiac') as $row) {
               printf('<li> <a href="%s?cmd=edit&id=%s">%s</a>',
                      $self,$row['id'],htmlentities($row['sign']));
           }
           */
      $offset = isset($_GET['offset']) ? intval($_GET['offset']) : 1;
      if (! $offset) { $offset = 1; }
      $per_page = 10;
      $total = $db->query('SELECT COUNT(*) FROM zodiac')->fetchColumn(0);
      $limitedSQL = 'SELECT * FROM zodiac ORDER BY id ' .
                    "LIMIT $per_page OFFSET " . ($offset-1);
      $lastRowNumber = $offset - 1;

           print '<a href="'.$self.'?cmd=add">Dodati redak</a><hr />';
           print '<ol>';
      foreach ($db->query($limitedSQL) as $row) {
          $lastRowNumber++;
          //print "{$row['sign']}"
          
          printf('<li> <a href="%s?cmd=edit&id=%s">%s</a>',
                      $self,$row['id'],htmlentities($row['sign']));
          echo ", {$row['symbol']} ({$row['id']}) <br/>\n";
      }
          print '</ol>';
      //
      indexed_links($total,$offset,$per_page);
      print "<br/>";
      print "(Prikazani retci $offset - $lastRowNumber od $total)";
           //
           break;
} // e n d 
 s w i t c h

3. Model- universal code ~ 70 lines :

 <?php
function build_query($db, $key_field, $fields, $table) {
    $values = array();
    if (! empty($_POST[$key_field])) {
        $update_fields = array();
        foreach ($fields as $field) {
            $update_fields[] = "$field = ?";
            // Assume data is coming from a form
            $values[] = $_POST[$field];
        }
        // Add the key field's value to the $values array
        $values[] = $_POST[$key_field];
        $st = $db->prepare("UPDATE $table SET " .
                   implode(',', $update_fields) .
                   "WHERE $key_field = ?");
    } else {
        // Start values off with a unique ID
        // If your DB is set to generate this value, use NULL instead
        $values[] = md5(uniqid());
        $placeholders = array('?');
        foreach ($fields as $field) {
            // One placeholder per field
            $placeholders[] = '?';
            // Assume the data is coming from a form
            $values[] = $_POST[$field];
        }
        $st = $db->prepare(
           "INSERT INTO $table ($key_field," .
              implode(',',$fields) . ') VALUES ('.
              implode(',',$placeholders) .')');
    }
    $st->execute($values);
    return $st;
}

function print_link($inactive,$text,$offset='') {
    if ($inactive) {
        print "<span class='inactive'>$text</span>";
    } else {
        print "<span class='active'>".
              "<a href='" . htmlentities($_SERVER['PHP_SELF']) .
              "?offset=$offset'>$text</a></span>";
    }
}

function indexed_links($total,$offset,$per_page) {
    $separator = ' | ';
    // 
    print_link($offset == 1, '<< Preth', max(1, $offset - $per_page));
    // print all groupings except last one
    for ($start = 1, $end = $per_page;
         $end < $total;
         $start += $per_page, $end += $per_page) {
             print $separator;
             print_link($offset == $start, "$start-$end", $start);
    }
    /* print the last grouping -
     * at this point, $start points to the element at the beginning
     * of the last grouping
     */
    /* the text should only contain a range if there's more than
     * one element on the last page. For example, the last grouping
     * of 11 elements with 5 per page should just say "11", not "11-11"
     */
    $end = ($total > $start) ? "-$total" : '';
    print $separator;
    print_link($offset == $start, "$start$end", $start);
    // 
    print $separator;
    print_link($offset == $start, 'Sljed >>',$offset + $per_page);
}

 

4. Multiple files upload OOP, namespaces & How to recognize mobile device – OOP, SPA, MVC domain style, PHP outside web doc root

HOME

Two scripts for multiple files upload – OOP, namespaces (see also article 10)

<?php
// 1. J:\zwamp64\vdrive\web\papl1\upload\index.php
use Classes\File\Upload; // = dir/subdir/classname 
                         // = namespace_name/clsname

// set the maximum upload size in bytes
$max = 10000 *      // 6 GB
        600 * 1024; // 600 KB
if (isset($_POST['upload'])) {
    // path to the upload folder : 
    //work: $destination = 'C:/upload_test/'; //or  __DIR__;
    $destination = DROOTPATH.DS.'FILE_TRANSFER'.DS; 
              // CONVENTION: below web doc.root
    require_once __DIR__.'/Classes/File/Upload.php'; // clsscript
    // -----------------------
    
    
    try {
        $loader = new Upload($destination);
        $loader->setMaxSize($max);
        $loader->allowAllTypes();
        $loader->upload();
        $result = $loader->getMessages();
    } catch (Exception $e) {
        echo $e->getMessage();
    }
}
?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Multiple Upload</title>
</head>

<body>
<?php
if (isset($result)) {
    echo '<ul>';
    foreach ($result as $message) {
        echo "<li>$message</li>";
    }
    echo '</ul>';
}
?>
<form action="" method="post" enctype="multipart/form-data">
  <p>
    <label for="image">Upload files (multiple selections permitted):
    </label>
    <input type="file" name="image[]" id="imageid" multiple>
  </p>
  <p>
    <input type="submit" name="upload" id="uploadid" value="Upload">
  </p>
</form>


<h3>Upload info ($_FILES)</h3>
<pre>
<?php if (isset($_POST['upload'])) 
{
  print_r($_FILES);
?>
Error levels in $_FILES array $err = $_FILES['file']['error'];
Error level Meaning
0 Upload successful $err = UPLOAD_ERR_OK
1 File exceeds maximum upload size specified in php.ini (default 2 MB)
  UPLOAD_ERR_INI_SIZE
  upload_max_filesize in php.ini eg 6400M (=6,4 GB)
  post_max_size = 64000M
  file_uploads = On  (see Local value in phpinfo -> Core section)
  upload_tmp_dir = "j:/wamp/tmp"  or whatever, default C:\Windows\Temp
  max_file_uploads = 20  for single request
  max_execution_time  60 sekundi<br />
  max_input_time  60 sek. (parse $_POST, $_GET, $_FILE arrays)
2 File exceeds size specified by MAX_FILE_SIZE in HTML form eg 6400M
  UPLOAD_ERR_FORM_SIZE
3 File only partially uploaded UPLOAD_ERR_PARTIAL
4 Form submitted with no file specified UPLOAD_ERR_NO_FILE
Error level 5 is currently not defined.
6 No temporary folder UPLOAD_ERR_NO_TMP_DIR (no destination_path)
7 Cannot write file to disk UPLOAD_ERR_CANT_WRITE (chmod 777)
8 Upload stopped by an unspecified PHP extension UPLOAD_ERR_EXTENSION
<?php } ?>
</pre>


<h3>$_POST parameters of this page</h3>

<table>
    <tr>
        <th>Parameter name</th>
        <th>Value</th>
    </tr>
    <?php 
     $count = 0; foreach ($_POST as $name => $value) { $count++ ?>
      <tr class="<?php echo $count % 2 == 0 ? 'alt' : ''; ?>">
        <td><?php echo htmlentities(stripslashes($name)) ?></td>
        <td><?php echo nl2br(htmlentities(stripslashes($value))) ?></td>
      </tr>
    <?php } 
echo '</table>';
?>

</body>
</html>
<?php
// 2. J:\zwamp64\vdrive\web\papl1\upload\Classes\File\Upload.php
namespace Classes\File; // dir/subdir
class Upload {

    protected $uploaded = [];
    protected $destination;
    protected $max = 51200;
    protected $messages = [];
    protected $permitted = [
        'image/gif',
        'image/jpeg',
        'image/pjpeg',
        'image/png'
    ];
    protected $typeCheckingOn = true;
    protected $notTrusted = ['bin', 'cgi', 'exe', 'js', 'pl', 'php'
           , 'py', 'sh'];
    protected $suffix = '.upload';
    protected $newName;
    protected $renameDuplicates;


    public function __construct($path) {
        if (!is_dir($path) || !is_writable($path)) {
            throw new \Exception("$path must be a valid
                 , writable directory.");
        }
        $this->destination = $path;
    }

    public function upload($renameDuplicates = true) {
        $this->renameDuplicates = $renameDuplicates;
        $uploaded = current($_FILES);
        if (is_array($uploaded['name'])) {
            // deal with multiple uploads
            foreach ($uploaded['name'] as $key => $value) {
                $currentFile['name'] = $uploaded['name'][$key];
                $currentFile['type'] = $uploaded['type'][$key];
                $currentFile['tmp_name'] = $uploaded['tmp_name'][$key];
                $currentFile['error'] = $uploaded['error'][$key];
                $currentFile['size'] = $uploaded['size'][$key];
                if ($this->checkFile($currentFile)) {
                    $this->moveFile($currentFile);
                }
            }
        } else {
            if ($this->checkFile($uploaded)) {
                $this->moveFile($uploaded);
            }
        }
    }

    public function getMessages() {
        return $this->messages;
    }

    public function getMaxSize() {
        return number_format($this->max/1024, 1) . ' KB';
    }

    public function setMaxSize($num) {
        if (is_numeric($num) && $num > 0) {
            $this->max = (int) $num;
        }
    }

    public function allowAllTypes($suffix = true) {
        $this->typeCheckingOn = false;
        if (!$suffix) {
            $this->suffix = '';  // empty string
        }
    }

    protected function checkFile($file) {
        $accept = true;
        if ($file['error'] != 0) {
            $this->getErrorMessage($file);
            // stop checking if no file submitted
            if ($file['error'] == 4) {
                return false;
            } else {
                $accept = false;
            }
        }
        if (!$this->checkSize($file)) {
            $accept = false;
        }
        if ($this->typeCheckingOn) {
            if (!$this->checkType($file)) {
                $accept = false;
            }
        }
        if ($accept) {
            $this->checkName($file);
        }
        return $accept;
    }

    protected function getErrorMessage($file) {
        switch($file['error']) {
            case 1:
            case 2:
                $this->messages[] = $file['name'] . ' is too big: (max: ' .
                    $this->getMaxSize() . ').';
                break;
            case 3:
                $this->messages[] = $file['name'] 
                     . ' was only partially uploaded.';
                break;
            case 4:
                $this->messages[] = 'No file submitted.';
                break;
            default:
                $this->messages[] = 
                   'Sorry, there was a problem uploading ' 
                   . $file['name'];
                break;
        }
    }

    protected function checkSize($file) {
        if ($file['error'] == 1 || $file['error'] == 2) {
            return false;
        } elseif ($file['size'] == 0) {
            $this->messages[] = $file['name'] . ' is an empty file.';
            return false;
        } elseif ($file['size'] > $this->max) {
            $this->messages[] = $file['name'] 
                     . ' exceeds the maximum size
                for a file (' . $this->getMaxSize() . ').';
            return false;
        } else {
            return true;
        }
    }

    protected function checkType($file) {
        if (in_array($file['type'], $this->permitted)) {
            return true;
        } else {
            if (!empty($file['type'])) {
                $this->messages[] = $file['name'] 
                    . ' is not permitted type of file.';
            }
            return false;
        }
    }

    protected function checkName($file) {
        $this->newName = null;
        $nospaces = str_replace(' ', '_', $file['name']);
        if ($nospaces != $file['name']) {
            $this->newName = $nospaces;
        }
        $extension = pathinfo($nospaces, PATHINFO_EXTENSION);
        if (!$this->typeCheckingOn && !empty($this->suffix)) {
            if (in_array($extension
                  , $this->notTrusted) || empty($extension)) {
                $this->newName = $nospaces . $this->suffix;
            }
        }
        if ($this->renameDuplicates) {
            $name = isset($this->newName) 
                       ? $this->newName : $file['name'];
            $existing = scandir($this->destination);
            if (in_array($name, $existing)) {
                // rename file
                $basename = pathinfo($name, PATHINFO_FILENAME);
                $extension = pathinfo($name, PATHINFO_EXTENSION);
                $i = 1;
                do {
                    $this->newName = $basename . '_' . $i++;
                    if (!empty($extension)) {
                        $this->newName .= ".$extension";
                    }
                } while (in_array($this->newName, $existing));
            }
        }
    }

    protected function moveFile($file) {
        $filename = isset($this->newName) 
                ? $this->newName : $file['name'];
        $success = move_uploaded_file($file['tmp_name']
              , $this->destination . $filename);
        if ($success) {
            $result = $file['name'] . ' was uploaded successfully to '
              . 'server directory : <br />'. $this->destination ;
            if (!is_null($this->newName)) {
                $result .= ', and was renamed ' . $this->newName;
            }
            $this->messages[] = $result;
        } else {
            $this->messages[] = 'Could not upload ' . $file['name'];
        }
    }
}

How to recognize mobile device – non OOP code

Excellent code to learn object oriented programming, model-view-controller code separation (structured programming), single page application (require scripts, not URL them), most code outside Apache doc root.

function findDevice() {
    $userAgent=strtolower($_SERVER['HTTP_USER_AGENT']);
$device=array('iphone','ipad','android','silk','blackberry', 'touch');
$deviceLength=count($device);

for($ii=0;$ii < $deviceLength;$ii ++) {
if(strstr($userAgent, $device[$ii])) {
return $device[$ii];
} else return 'desktop/laptop';
}
}

OOP, SPA, MVC domain style, PHP outside web doc root

Display ibrowser properties (view class methode):

  1. Device = desktop/laptop
  2. Browser = firefox
  3. userAgent = mozilla/5.0 (windows nt 6.3; win64; x64; rv:25.3) gecko/20150323 firefox/31.9 palemoon/25.3.1

Or display one property – returned from getter: Device = desktop/laptop

About OOP programs

  1. CODE INSIDE APACHE DOC ROOT (this page script) :  kod  edit  phpinfo
    INCLUDED CODE FROM OUTSIDE APACHE DOC ROOT :
  2. code behind this page script (model .pcls) :  kod  edit  phpinfo
  3. code config – set up :  kod  edit  phpinfo
  4. code helper (util) :  kod  edit  phpinfo
  5. /**
    * This page URL $idxurl = 
    *    http://dev:8083/inc/utl/get_ibrowser_device.php
    *   displays o u t p u t  o f  server script 
    *          $idx.DS.$idxscript =
    *   J:\dev_web\htdocs\inc\utl\get_ibrowser_device.php, 
    *           which contains :
    *      MODEL_fn_call from public fn __ c o n s t r u c t ( )
    *      protected_VIEW_fn_call from public fn
    *      CONTROLLER_code before c l a s s  C l i e n t
    *
    * server script i n c l u d e s scripts which are outside Apache doc root :
    *   1. config $confglob                : require_once('J:\dev_web\inc\confglob.php');
    *   2. helper (util) $edrun            : require_once('J:\dev_web\inc\utl\kod_edit_run.php');
    *   3. code behind (class) $codebehind : require_once('J:\dev_web\inc\utl\get_ibrowser_device.pcls');
    *
    * $confglob contains  P H P  s e t  u p :
    *    ini_set('display_errors','2');
    *    ERROR_REPORTING(E_ALL);
    **/

SCRIPTS:

  1. J:\dev_web\htdocs\inc\utl\get_ibrowser_device.php
    // 1. a d r e s s e s :
    if (!defined('DS')) define('DS',DIRECTORY_SEPARATOR);
    if (!defined('CONFGLOB_DIR')) define('CONFGLOB_DIR',
    realpath($_SERVER['DOCUMENT_ROOT'].'/../inc'));
    $apl = dirname(dirname(__DIR__)).DS.’htdocs’; // *** !!! *** ONLY YOU TO SET UP, used for link
    $idx = __DIR__ ; $idxscript = basename(__FILE__) ;// 2. i n c l u d e s :
    $confglob   = CONFGLOB_DIR.DS.’confglob.php’;
    $edrun      = CONFGLOB_DIR.DS.’utl’.DS.’kod_edit_run.php’;
    $codebehind = CONFGLOB_DIR.DS.’utl’.DS.’get_ibrowser_device.pcls’;
    require_once($confglob);   // c o n f i g
    require_once($edrun);      // h e l p e r
    require_once($codebehind); // m o d e l// 3. c o n t r o l l e r :
    $trigger = new Client(); // trigger becouse can direct output view// ************** e n d  c o n t r o l l e r  p r o g r a m// v i e w :
    ?>
    <!DOCTYPE html>
    <html lang=”hr”>
    <head>
    <title>Is mobile</title>
    <meta content=”text/html; charset=utf-8″; http-equiv=”content-type”>
    <!–base href=’/’–>
    <link rel=’stylesheet’ href=’lib/bootstrap/dist/css/bootstrap.min.css’ />
    <link rel=’stylesheet’ href=’src/bootstrap.min.css’ />
    </head>
    <body><h2>How to recognize mobile device – non OOP code</h2>
    <p><code><code><span style=”color:#000000; “><span style=”color:#007700; “>function </span><span style=”color:#0000BB; “>findDevice</span><span style=”color:#007700; “>() {<br>
    </span></span><code><span style=”color:#000000; “><span style=”color:#007700; “>    $</span><span style=”color:#0000BB; “>userAgent</span><span style=”color:#007700; “>=</span><span style=”color:#0000BB; “>strtolower</span><span style=”color:#007700; “>(</span><span style=”color:#0000BB; “>$_SERVER</span><span style=”color:#007700; “>[</span><span style=”color:#DD0000; “>’HTTP_USER_AGENT'</span><span style=”color:#007700; “>]);<br>
    </span><span style=”color:#FF8000; “>
    </span><span style=”color:#007700; “>$</span><span style=”color:#0000BB; “>device</span><span style=”color:#007700; “>=array(</span><span style=”color:#DD0000; “>’iphone'</span><span style=”color:#007700; “>,</span><span style=”color:#DD0000; “>’ipad'</span><span style=”color:#007700; “>,</span><span style=”color:#DD0000; “>’android'</span><span style=”color:#007700; “>,</span><span style=”color:#DD0000; “>’silk'</span><span style=”color:#007700; “>,</span><span style=”color:#DD0000; “>’blackberry'</span><span style=”color:#007700; “>, </span><span style=”color:#DD0000; “>’touch'</span><span style=”color:#007700; “>);<br>$</span><span style=”color:#0000BB; “>deviceLength</span><span style=”color:#007700; “>=</span><span style=”color:#0000BB; “>count</span><span style=”color:#007700; “>($</span><span style=”color:#0000BB; “>device</span><span style=”color:#007700; “>);<br>
    </span></span></code><span style=”color:#000000; “><span style=”color:#007700; “><br>
    for(</span><span style=”color:#0000BB; “>$ii</span><span style=”color:#007700; “>=</span><span style=”color:#0000BB; “>0</span><span style=”color:#007700; “>;</span><span style=”color:#0000BB; “>$ii </span><span style=”color:#007700; “>&lt; $</span><span style=”color:#0000BB; “>deviceLength</span><span style=”color:#007700; “>;</span><span style=”color:#0000BB; “>$ii </span><span style=”color:#007700; “>++) {<br>
    if(</span><span style=”color:#0000BB; “>strstr</span><span style=”color:#007700; “>($</span><span style=”color:#0000BB; “>userAgent</span><span style=”color:#007700; “>, $</span><span style=”color:#0000BB; “>device</span><span style=”color:#007700; “>[</span><span style=”color:#0000BB; “>$ii</span><span style=”color:#007700; “>])) {<br>
    </span><span style=”color:#FF8000; “>        </span><span style=”color:#007700; “>return $</span><span style=”color:#0000BB; “>device</span><span style=”color:#007700; “>[</span><span style=”color:#0000BB; “>$ii</span><span style=”color:#007700; “>];<br>
    } else return </span><span style=”color:#DD0000; “>’desktop/laptop'</span><span style=”color:#007700; “>;<br>
    }<br>
    }</span></span></code></code></p>
    <h2>OOP, SPA, MVC domain style, PHP outside web doc root</h2>
    <p>Display ibrowser properties (view class methode):<br />
    <?php $trigger -> out_vew_ibrowse_params() ; ?>
    </p>

    <p>Or display one property – returned from getter: Device =
    <?php echo $trigger->get_device(); ?> </p>

    <h2>About OOP programs</h2>
    <ol>
    <li>CODE INSIDE APACHE DOC ROOT (this page script) :
    <?php kod_edit_run(
    $idx       // script_dir
    , $idxscript // script
    , MDURL); ?>
    <br>
    <br>
    INCLUDED CODE FROM OUTSIDE APACHE DOC ROOT : <br>
    <li>code behind this page script (model .pcls) :
    <?php kod_edit_run(
    dirname($codebehind)  // script_dir
    , basename($codebehind) // script
    , MDURL); ?>
    <li> code config – set up :
    <?php kod_edit_run(
    dirname($confglob)  // script_dir
    , basename($confglob) // script
    , MDURL); ?>
    <li>code helper (util) :
    <?php kod_edit_run(
    dirname($edrun)  // script_dir
    , basename($edrun) // script
    , MDURL); ?>
    <li> <pre>/**
    * This page URL $idxurl = <a href=”<?php echo $idxurl; ?>”><?php echo $idxurl; ?><a>
    *   displays o u t p u t  o f  server script $idx.DS.$idxscript =
    *   <?php echo $idx.DS.$idxscript; ?>, which contains :
    *      MODEL_fn_call from public fn __ c o n s t r u c t ( )
    *      protected_VIEW_fn_call from public fn
    *      CONTROLLER_code before c l a s s  C l i e n t
    *
    * server script i n c l u d e s scripts which are outside Apache doc root :
    *   1. config $confglob                : require_once(‘<?php echo $confglob; ?>’);
    *   2. helper (util) $edrun            : require_once(‘<?php echo $edrun; ?>’);
    *   3. code behind (class) $codebehind : require_once(‘<?php echo $codebehind; ?>’);
    *
    * $confglob contains  P H P  s e t  u p :
    *    ini_set(‘display_errors’,’2′);
    *    ERROR_REPORTING(E_ALL);
    **/ </pre>
    </ol>

    <?php
    class Client
    {
    private $IbrowserProp;

    // M O D E L :
    public function __construct()   {
    $this->IbrowserProp = new getIbrowserProp();
    } // e n d  p u b l i c  f n  _ _c o n s t r u c t ( )

    public function get_device() {
    return  $this->IbrowserProp->findDevice() ;
    } // e n d

    // V I E W :
    public function out_vew_ibrowse_params() {
    $this->vew_ibrowse_params();
    } // e n d
    protected function vew_ibrowse_params()  // public private protected
    {
    echo ‘<ol>’;
    echo ‘<li>Device = ‘    . $this->IbrowserProp->findDevice() . ‘<br/>’;
    echo ‘<li>Browser = ‘   . $this->IbrowserProp->findBrowser() . ‘<br/>’;
    echo ‘<li>userAgent = ‘ . $this->IbrowserProp->getUserAgent() . ‘<br/>’;
    echo ‘</ol>’;
    } // e n d  p u b l i c  f n  _ _c o n s t r u c t ( )

    } // e n d  c l a s s  C l i e n t

    ?>
    </body>
    </html>

  2. J:\dev_web\inc\utl\get_ibrowser_device.pcls
    <?php
    ini_set("display_errors","2");
    ERROR_REPORTING(E_ALL);
    //User agent as property of object
    class getIbrowserProp
    {
    private $userAgent;
    private $device;
    private $browser;
    private $deviceLength;
    private $browserLength;
    public function __construct()
    {
    $this->userAgent=strtolower($_SERVER[‘HTTP_USER_AGENT’]);
    //$this->userAgent=strtolower($this->userAgent);$this->device=array(‘iphone’,’ipad’,’android’,’silk’,’blackberry’, ‘touch’);
    $this->browser= array(‘firefox’,’chrome’,’opera’,’msie’,’safari’,’blackberry’,’trident’);
    $this->deviceLength=count($this->device);
    $this->browserLength=count($this->browser);
    }
    public function findDevice()
    {
    for($ii=0;$ii < $this->deviceLength;$ii ++)
    {
    if(strstr($this->userAgent,$this->device[$ii]))
    {
    //$device = $this->device[$ii];
    //if (is_null($device)) $device = ‘desktop/laptop’;
    //return $device;
    return $this->device[$ii];
    } else return ‘desktop/laptop’;
    }
    }//public function get_device() {
    //    return $this->vew_ibrowse_params();
    //} // e n dpublic function findBrowser()
    {
    for($ii=0;$ii < $this->browserLength;$ii ++)
    {
    if(strstr($this->userAgent,$this->browser[$ii]))
    {
    return $this->browser[$ii];
    }
    }
    }
    public function getUserAgent()
    {
    return($this->userAgent);
    }
    }?>
  3. J:\dev_web\inc\confglob.php
    <?php
    // J:\dev_web\inc\confglob.php - NOT WEB ACCESSIBLE
    //    - SAME LEVEL AS APACHE DOC ROOT
    ini_set("display_errors","2");
    ERROR_REPORTING(E_ALL);
    $md=realpath($_SERVER[‘DOCUMENT_ROOT’]);// 1. rel.adresses are ok for both  p a t h s  &   u r l s :
    $idxrel = str_replace($md,”, $idx); // str_replace(DS,’/’,
    $aplrel = str_replace($md,”, $apl); // str_replace(DS,’/’,// 2. u r l s  – s u b a p l,  a p l,  m d (main doc.root = Apache doc.root):
    $mdurl =’http://’.$_SERVER[“SERVER_NAME”].’:’.$_SERVER[“SERVER_PORT”];
    if (!defined(‘MDURL’)) define(‘MDURL’, $mdurl);
    $idxurl = $mdurl.str_replace(DS,’/’,$idxrel).’/’.$idxscript;
    $aplurl = $mdurl.’/’.substr(str_replace(DS,’/’,$aplrel),1);
    $imgurl = $mdurl.’/inc/img’;
  4. J:\dev_web\inc\utl\kod_edit_run.php
    <?php
    function kod_edit_run($script_dir_path, $script_name, $web_docroot_url)
    {
    $ds = DIRECTORY_SEPARATOR;
    echo <<< EOKOD
    <a href="$web_docroot_url/inc/utl/showsource.php
    ?file=$script_dir_path$ds$script_name
    &line=1&prev=10000&next=10000
    "  target="_blank">&nbsp;kod</a>
    <a href=”$web_docroot_url/inc/utl/edservertxt.php
    ?file=$script_dir_path$ds$script_name
    ” target=”_blank”>&nbsp;edit</a><a href=”$web_docroot_url/phpinfo_inc.php
    ” target=”_blank”>&nbsp;phpinfo</a>
    EOKOD;
    /* call it so:
    kod_edit_run(
    $idx       // script_dir_path
    , $idxscript // script_name
    , MDURL);    // web_docroot_url = (Apache) web server URL
    */
    }