10. Real life application “Messages” PHP PDO, AJAX+jQuery CRUD&filter (thema, blog, forum, CMS, builetinboard, Skype replacement)

Revised 2020.03.26

HOME  7. Jan. 2018 I uploaded code on Github https://github.com/slavkoss/fwphp – much different then in this article title – see below “All we need…”. This old B12phpfw version 3. is more complicated but if you like it could be simplified. I think version 6 on Github is best, simplest.

I think it is my last word about routing which I do not like in my previous articles here. Basic ideas are same in all my articles (code skeleton like Oracle Forms) Previous articles were tests of different technologies which made my conclusion :

All we need for MVC OOP PHP framework is (beside love) : to be mazohist what I try to minimize here, which is reason for this blog :

  1. routing / dispatching (last code as in Mini3 PHP framework https://github.com/panique/mini3)  based on URL query array
  2. PHP PDO CRUD
  3. CSS ~ bootstrap or own ~200 lines
  4. for blogging rich text web editor like : markdown SimpleMDE or html Summernote
  5. no or minimal Javascript, no AJAX, no JS frameworks : React, jQuery, AngularJS…  and simmilar overprogrammed libraries

I would use PHP and JS frameworks if they had better help, but they all seem (partially) like cat in bag, vaporware. It is pity so much work and at end we have application, not developing SW. Only author and his friends understand it.

Same is fate of Oracle Forms, new versions, after 6i, with 3-4 servers. It is application, not developing SW, it is why nobody starts new projects with Oracle Forms and Oracle advises Java developer also written in C called Java and better for evil empires. There is no sin to make new SW – old should be refactored – recoding all the time – see in Google search “extreme programming”.

Kae Verens published 2010 year nice book -CMS Design Using PHP and jQuery-. It has same problem as 90% WEB learning resources : smarty, and rich text ckeditor do not work and I did not found easy way to use newer version (Kae did not explain such problem). -Path science- is very poor – based on -all is in site-s document root dir. What if we do not have more virtual hosts (sites). It is very strange that this basics (and some other) are unfinished because this book is based on 10 years real CMS development.

 

Old versions

15.october 2017 I uploaded complete test site (with php.ini and Apache config files) in 1_tests.zip, it is old code not of interest, except some code snippets.

14.august 2016   I uploaded 3sites .rar – see HOME page for downloads, it is old code not of interest, except some code snippets. **4.august 2016** -Messages- real life application added **nicEdit** rich text editor (after testing ~10 RTE).

9. Main development, test and production menu (& 3 sites) – PHP 7 RC5, Oracle 11g on Windows 10 all 64 bit

HOME – Download Win10 64 bit AMP config and three sites php scripts.

14.8.2016 I changed a lot, see my article 10. This article 9 can be used only for explanations but everything is in code in 1_3sites_mnu_crud_ver1.rar.

22.11.2015 code refactored – i uploaded sites code version 3, (SINGLETON UTILS-HELPERS CLASS, config_site.php…).

Scripts config_site.php, utls.php, utls_glbsetpg.php and lsweb.php explain most important changes. I hope this is final code skeleton, ver4 will be bug fixes and cosmetic updates. (In ver3 I tested only most important lsweb.php but testing others is good exercise – and it shows how difficult this job is).

Statements (functions) flow article (not yet uploaded) should link together all I wanted to say.

Every (vitual) host – I have them 3: dev, test, production – should have in its web site doc root script which tells web server where are resources which are outside doc tree (global fns, settings, css, imgs)

(Problem with resources outside web doc tree is that we need file_get_contents() fn which is disabled by some inet providers. But if you want own SOAP server eg for direct b2b copying einvoice xml – then every your user should have own web site (bad news for some not needed service brokers – posrednici).)

Most important are:
1. “path science” – unexplained enough in learning materials I met (for SPA script very different than for URL called script).
2. Statements (functions) flow (to understand SW & for debugging) – I shall soon write article 10 or 11 (?) about this (SOAP examples for copying xml across inet will also be soon).

I can not understand SPA & frameworks without above two explained. To me seems that framework authors intentionally do not explain important things about their scripts coding (there is to much commercial interests in free SW) – so we have 100 frameworks instead 5-6.

I am shure that Utils class should be, settings should be in global_config.php both outside web doc tree. Local settings in doc tree should owerwrite global.

Eg my : J:\awww\apl\dev1\config_site.php contais:

 < ?php
 // development site (virtual host on home PC) :
 // J:\awww\apl\dev1\config_site.php
//$_SERVER['DOCUMENT_ROOT'] = J:\awww\apl\dev1\
 // yii2 advanced site (virtual host on home PC) :
 // J:\awww\apl\dev1\zfw\yii205\frontend\web\config_site.php
 // J:\awww\apl\dev1\zfw\yii205\aplmy\backend\web\config_site.php
 // realpath
 use utlmoj\utlmoj as utl;
 $ds = DIRECTORY_SEPARATOR;
 /**
 * CONVENTION: sitedocroot/../zinc = eg J:/awww/apl/zinc
 * site does not know where (outside site doc tree) are global resources
 * (util scripts, css, img for all sites), so we must assign
 * 1. GLOBAL RESOURCES (INCLUDES) FOLDER :
 */
 $gloresdir = realpath($_SERVER['DOCUMENT_ROOT'].'/../zinc'); // 1.
 require_once($gloresdir.'/utls.php'); // 2. util (helper) scripts
 $utl=utl::uget(); // 3. get or create helper fns object (singleton)
 require_once($gloresdir.'/utls_glbsetpg.php'); // 4. global page properties
 /**
 * IN APPL (IN SPA) IF IT NEEDS C R U D :
 * 2. g l o b a l C R U D :
 * $dbi = 'sqlite'; $dsn = 'cars_makes_names_savings.sqlite';
 * require_once($gloresdir.$ds.'db_conn.php');
 * db_conn.php does:
 * 1. require_once($gloresdir.'/klase/dbi.php');
 * 2. require_once($gloresdir.'/tbl/zodiac_mdl.php);
 * CONVENTION for M D L of concrete tbl :
 * require_once($gloresdir.'/tbl/'
 * .str_replace('.php','_mdl.php', basename($curpgpath)));
 * 3. template crud script :
 * require_once($gloresdir.$ds.'crud.php');
 */
 // ******************************************
 //exit();
 ?>

I am not shure if Utils class should have static or non static or both methods & properties (because you need many years PHP programming experience to be shure). Properties of this two working ways are not clear to me and unexplained enough in my learning sources.

Pitty that somebody – PHP expert – which I am not – does not explain this somewhere.

2.Sept.2015 – site_ver2.rar – improved code for all scripts, eg for lsweb.php and added some dir icons and better presented/explained site dir structure (see awww_DIR_NOT_VISIBLE_TO_ME.txt).

This article is enough for (advanced) beginning PHP programming Oracle and SQLite CRUD. 
Articles 1 to 8 are supplementary info.

This article unites my posts 3. Zwamp menu and 5. CRUD simple table (example 1) with refactored code for 5. CRUD and my (I hope) final site directories structure.

Most important examples in this article (others are in site_verx.rar):

Example Šifrarnik – is not finished, but shows much. Model is table (id, few_columns)

Example web server directories listing – dir items listing can be extended with row filters, sorts, downloads… but so as it is is very useful for web development.
Model (input data) is DirectoryIterator().
I find lsweb.php very useful for web development:
J:\awww\apl\dev1\zinc\utl\ls.php          http://dev1:8083/zinc/utl/ls.php  (or with ?dir=J:\awww\apl\dev1)
J:\awww\apl\dev1\zinc\utl\lsweb.php

Code for article 3. Zwamp menu is also contained in  site_verx.rar – it is to complicated for real life sites (except if you like something like)  but is excellent for learning PHP.

 

Oracle example 1

Model (input data) is simple table (šifrarnik) (id, few_columns), but more than 50% programming techniques can be learned on this and next example.


J:\awww\apl\dev1\zinstalac\ddl\DDL_selfjoin.sql:
-- winkey+X -> Comm.prompt admin
C:
cd C:\oraclexe\app\oracle\product\11.2.0\server\bin
sqlplus hr/hr
sho user
CREATE TABLE ZODIAC (
  ID          NUMBER(10) NOT NULL,
  SIGN        VARCHAR2(11),
  SYMBOL      VARCHAR2(13),
  PLANET      VARCHAR2(7),
  ELEMENT     VARCHAR2(5),
  START_MONTH INTEGER,
  START_DAY   INTEGER,
  END_MONTH   INTEGER,
  END_DAY     INTEGER,
  PRIMARY KEY(ID)
);

CREATE SEQUENCE ZODIAC_SEQ;
CREATE or replace TRIGGER ZODIAC_PREINS_TRIG 
BEFORE INSERT ON ZODIAC 
FOR EACH ROW
BEGIN
  -- PRIOR TO 11G :
  SELECT ZODIAC_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
  -- 11G :
  --:NEW.MSG_ID := ZODIAC_SEQ.NEXTVAL;
END;
/
sho err

DML :
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);

Scripts directory structure (SPA, MVC domain style)

SPA means scripts are included, only exceptionally URL called. So scripts see all $ variables as own.

In SPA is not posible using relative page adresses because path relative to included script is not relative to includer (SPA) script  – adresses syntax explained here must be used !! To learn dirs science is one of most difficult PHP beginning parts, and allmost NOT EVEN MENTIONED in PHP learning materials.

MVC domain style means Scripts directory structure is “one form (application) one dir (& its subdeirs if needed)”. MVC is basically old structured programming:

  1. initialize (bootstrap, config),
  2. manage (ctr),
  3. input (model),
  4. output (view).

It is interesting that no one MVC promotor mentions this simple truth. Names are most important, but working means not (only) inventing new names but understand+explain.

DDLs are in J:\awww\apl\dev1\zinstalac\ddl\,  eg above  DDL_selfjoin.sql contain also selfjoin DDL & for SQLite.

Global config scripts (.php, .css, .js…most important of all types !) are in J:\awww\apl\dev1\zinc\,  eg  utls.php.

Below zinc are dirs: utl (helpers), slike (img), js…

Three sites (Apache virtual hosts on home PC):

Development URL is http://dev1:8083/ is Windows dir J:\awww\apl\dev1.

Production URL is http://pro1:8083/  is Windows dir J:\awww\apl\pro1.

Simmilar are both test site adresses.

 

After huderts Oracle Forms 6i created from scratch (from existing form ctrl+c,v) in more then 15 years it seems to me, whatever they say :), for PHP & Oracle should be :

CONVENTIONS FOR BETTER SCRIPTS VISIBILITY

  1. No camelcase becouse name higher_lower is (to me) better visible then camelcase higherLower
  2. Scripts sufixes :
    1. controller scripts have no sufix
    2. other scripts :
      1. model scripts: _mdl, _val (validation scripts)
      2. view scripts: _frm, _tbl, _rep
  3. ctr / bootstrap scripts are NOT named index.php, but are named simmilar to DB table name which they (CRUD) manage.Beside script visibility, this enables us to have more/all small tables crud (forms) scripts in one dir.To many dirs is not practical and old principle “one form (application) one dir (& its subdeirs if needed)” is newest fashion (2015 year).
  4. GLOBAL configuration scripts are in $CNFGD=J:\awww\apl\dev1\zinc, eg $CNFGD.$DS.utls.phputls.php (with help of config not static class & namespace) defines PHP $  (adress) variables for all included scripts (SPA !!) (no constants except pi and simmilar). Conf. vars must be defined as $ vars in utls.php and not as concatenation of dirs in all index.php scripts, becouse :
    IF DIR TREE CHANGES -> WE CHANGE ONLY ONE PLACE: utls.php
  5. Some strange names are better for search or for name conflicts (eg zinc instead of includes, chcons instead configclass, this example zodiac…)

 

Example 2: Web server directories listing programs

Model (input data) is DirectoryIterator() dir items listing.

I find them very useful for web development:
J:\awww\apl\dev1\zinc\utl\ls.php          http://dev1:8083/zinc/utl/ls.php  (or with ?dir=J:\awww\apl\dev1)
J:\awww\apl\dev1\zinc\utl\lsweb.php

 Conclusion

I did not see advantages in my testings compared with programming techniques I explained in this article (and in other before):

  1. AngularJS 1.4.3 CRUD with Oracle 11g
  2. Yii 2.0.6 with PHP 7 beta 3
  3. FatFree PHP fw

But frameworks above:

  1. no Oracle DBI example for normal people given
  2. It seems to me they are unfinished – always in development (uncompatible new versions), always new fws appear.
  3. slower ( min ~ 150 kB code with uncertain future included),
  4. more complicated – another (more) programming languages
  5. They do not even mention reporting (php reports from Eustáquio Rangel or similar) or tab key -> enter key which I made with few lines Javascript code (see site_ver1.rar J:\awww\apl\dev1\zinc\key_pressed.js).
  6. I doubt if they are more productive – learn time for programming techniques I explained in this article is ~ as for them  (lot of time 🙂 )
  7. to many incompatibilities uncertain future (yii2, ng 2),

Until authors of fw sw convince us on CRUD examples I give in this article + master-detail example I can not believe their to simple / not clear CRUD examples.


~~~utls.php $test=1 ~~~end script J:\awww\apl\dev1\index.php   source   phpinfo~~~

 

8. Understand AngularJS (ng) ver. 1.4.3 & PHP server script Get Emp from Oracle DB 11g

HOME   – Download code

2.Sept.2015  I uploaded  ng_understand.rar – it contains now all CRUD code.

It seems to me that ng is very nice but brings no advantage – PHP is enough, and problem is to learn two programming techniques, to include ng (minimum 150 kB) …

We fill in $scope ng object with Oracle DB 11g data with help of
AJAX “client-server” PHP script get_emp_data.php (hr user).

Output page shows (looks better with .css included in zip):

ng & PHP server script Get Emp from oracle DB 11g

Search:

EmpId LastName HireDate
DML: select EMPLOYEE_ID, LAST_NAME, HIRE_DATE from (select EMPLOYEE_ID, LAST_NAME,to_char(HIRE_DATE,’RRRR.DD.MM’) HIRE_DATE from EMPLOYEES order by LAST_NAME) where ROWNUM < 11
167 Banda 2008.21.04
116 Baida 2005.24.12

This script get_emp.html 15.7.2015 says:

  1. Script get_emp.html is same as index.html – understand ng.
    Included is app.js code for quick testing.
    Added are few additional lines to fill in $scope object from Oracle DB 11g data with help of
    AJAX “client-server” called PHP script get_emp_data.php.
  2. Search field works after every character typed in.
  3. Click on eg HireDate column title works but not for both sorts asc/desc – I shall do this later.
  4. I shall add later to this post (to ng_understand.zip)
    all CRUD functions in programming techniques :
    ng – PHP server scripts called with AJAX (client-server web programming technique) – Oracle DB 11g.

    // get_emp.html contains app.js code for quick testing
    // HH24:mi:ss
    $http.get("get_emp_data.php?sqlStr="
      +"select EMPLOYEE_ID, LAST_NAME, HIRE_DATE from"
      +" (select EMPLOYEE_ID,  LAST_NAME"
      +   ",to_char(HIRE_DATE,'RRRR.DD.MM') HIRE_DATE
      + " from EMPLOYEES order by LAST_NAME)"
      +" where ROWNUM < 11"
    )
    .success(
        function(response) {
          $scope.myData  = response;
          $scope.reverse = true;
        }
    )  
    .error(
       function() {
                
            }
    ) ;
  5. AJAX “client-server” called PHP script get_emp_data.php has ~five important statements:
    <?php
    /*
    http://dev1:8083/my_dev/test/...get_emp_data.php
    http://dev1:8083/my_dev/test/...get_emp_data.php?sqlStr=select...
    J:\awww\apl\dev1\my_dev\test\...get_emp_data.php
    */
    $pdo=new PDO('oci:dbname=sspc/XE','hr','hr');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $stmt = $pdo->prepare($_GET['sqlStr']);
    /*
    $stmt = $pdo->prepare(
      'select EMPLOYEE_ID,  LAST_NAME'
      .',to_char(HIRE_DATE,\'DD.MM.RRRR\') HIRE_DATE' // HH24:mi:ss
      .' from EMPLOYEES where ROWNUM < 16'
    );
    */
    
    $stmt->execute();
    $arr = array();
    while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
       $arr[] = $row;
    }
    //echo '<pre>'; print_r($arr); echo '</pre>';
    //$arr = $_GET; // {"sqlStr":"select..."}
    
    $arr[] = Array (
        'EMPLOYEE_ID' => "DML:"
       ,'LAST_NAME'   => $_GET['sqlStr']
       ,'HIRE_DATE'   => ''
    );
    //{"EMPLOYEE_ID":"114","LAST_NAME":"Raphaely","HIRE_DATE":"07.12.2002"},
    //{"EMPLOYEE_ID":-1,"LAST_NAME":"select...","HIRE_DATE":""}]
    $json_response = json_encode($arr);
    echo $json_response;
    ?>

7. ORACLE 11g XE PERSISTENT DB CONNECTION POOLED (ESTABLISHED WITH PHP PDO OR E.Rangel’s PDOOCI)

HOME  

4.Mart 2017

Two scripts below (dbconn and tbl) are basic skeleton for any CRUD (PHP PDO) program.

Here is only R=Read (with filter and sort) of PDO CRUD, no JS.
Later I shall upload OOP MVC version with CUD of CRUD and JS msg and yesno dialog, but :
code  here is first MUST LEARN about PHP PDO CRUD.

You can use any your suitable table for testing and Oracle 11g as I did or MySQL or SQLite.
Code changes are not difficult and are minimal because PDO syntax is used.

I think Mini3 is best learning code for learning OOP MVC.
Simplest JS msg code (from OOP MVC version with CUD which I am testing based on
https://github.com/panique/mini3 which has no JS),
generated in PHP on server side (eg for delete confirmation or debugging as code below) :

// snippet from J:\awww\apl\dev1\papl1\mini3fw\application\Core\Application.php
// see https://github.com/panique/mini3
if (TEST)
{ 
?><SCRIPT LANGUAGE="JavaScript"><!-- Begin
    //alert(t1+"\n"+t2+"\n"+t3+"\n"+t4+"\n"+t5+"\n"+t6+"\n"+txt_srvgen); 
    alert( '<?php echo str_replace('<br>','\n',str_replace('<br>','\n',
                'ctr='.$this->url_controller
               .'<br>akc='.$this->url_action
               .'<br>akc.params='.json_encode($this->url_params)
               ));?>'
   );
  // End --></SCRIPT>
<?php }

 

<?php
// J:\awww\apl\dev1\inc\db\dbconn_PDOOCI_mer.php
use PDOOCI\PDO as PDO;

define("DSN", "sspc1/XE:pooled;charset=UTF8"); // UTF8 EE8MSWIN1250
define("USR", "uuu");  define("PSW", "uu");
//require_once $_SERVER['DOCUMENT_ROOT'].'/vendor/autoload.php'; //E.Rangel's pdooci in ROOTDIR
require_once $_SERVER['DOCUMENT_ROOT'].'/inc/db/PDO.php';  //or put it in same dir as tbl script
$options = array(PDO::ATTR_PERSISTENT => true);
try{
  $db = new PDO(DSN, USR, PSW, $options);
  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                if(TEST) {echo '<b>'.__FILE__.' SAYS:</b><pre>';
                  echo 'PDOOCIonOCI8 connection successful, DSN='.DSN.', USR='.USR.', PSW='.PSW;
                  echo '</pre>'; }
}catch (PDOException $ex){
  echo '<b>'.'***ERROR dbconn DSN='.DSN.' '.$ex->getMessage().'</b>';
}
<?php
// J:\awww\apl\dev1\pdev1\01info\02izdatnica_tbl.php

//                     IZDATNICA = INVOICE

defined('TEST') or define('TEST', '1');
defined('DS') or define('DS', DIRECTORY_SEPARATOR);
defined('ROOTURL') or define('ROOTURL', 
            //$_SERVER['REQUEST_SCHEME'] . ':'. 
            '//'
            . $_SERVER['SERVER_NAME'] //WEBADRESA sspc1, localhost, dev1
            .':'.$_SERVER['SERVER_PORT'] );
defined('CSSURL') or define('CSSURL', ROOTURL.'/'.'inc/'.'css/sitemoj.css');

require_once $_SERVER['DOCUMENT_ROOT'].'/inc/db/dbconn_PDOOCI_mer.php';
//require_once 'z_local_dbconn_PDOOCI_mer.php';

  // ******************************************
  // P a r a m e t e r s
  // ******************************************
  
// Filters are sticky values :
$p_imekup = ''; if (isset($_GET['imekup'])) $p_imekup = $_GET['imekup'];
$p_tipd = 9999; if (isset($_GET['tipd'])) $p_tipd = $_GET['tipd'];

// S o r t  clause is sticky value :
$p_sortdml = 'i.SIFRA_TIP_DOC,length(i.BROJ_IZDATNICE) desc,i.BROJ_IZDATNICE desc';
$p_sort = 'SIFRA_TIP_DOC'; // default
if (isset($_GET['sort'])) $p_sort = $p_sortdml = $_GET['sort'];

    switch ($p_sort)
    {
    case 'BROJ_IZDATNICE':
      $p_sortdml = 'length(i.BROJ_IZDATNICE) desc,i.BROJ_IZDATNICE desc';
      break;
    case 'IME_KUPCA':
      $p_sortdml = 'k.IME_KUPCA,length(i.BROJ_IZDATNICE) desc,i.BROJ_IZDATNICE desc';
      break;
    case 'SIFRA_TIP_DOC':
      default:
$p_sortdml = 'i.SIFRA_TIP_DOC,length(i.BROJ_IZDATNICE) desc,i.BROJ_IZDATNICE desc';
      break;
    } // e n d  s w i t c h


if (isset($_GET['search'])) 
{
  // ******************************************
  // M o d e l 
  // ******************************************
  try
  {
        //require_once $_SERVER['DOCUMENT_ROOT'].'/inc/db/dbconn_PDOOCI_mer.php';
        //require_once 'z_local_dbconn_PDOOCI_mer.php';
        $sql = '
          SELECT i.BROJ_IZDATNICE, i.datum
               , to_char(i.datum,'.'\'RRRR.MM.DD\''.') DATUM_FMT, i.SIFRA_TIP_DOC
               , k.ime_kupca ime_kupca, tipd.opis OPIS_DOK
          FROM t_izdatnica i, t_kupac k, t_tip_doc tipd
          WHERE i.SIFRA_KUPCA   = k.SIFRA_KUPCA
            AND i.SIFRA_TIP_DOC = tipd.SIFRA_TIP_DOC
            AND upper(k.ime_kupca) LIKE upper(:imekup) 
            AND i.SIFRA_TIP_DOC = decode(:tipd, '.'9999'.',i.SIFRA_TIP_DOC, :tipd)
          ORDER BY '.$p_sortdml ; // , i.napomena

        $stmt = $db->prepare($sql);
        $stmt->bindValue(':imekup', '%'.$p_imekup.'%');
        $stmt->bindParam(':tipd', $p_tipd, PDO::PARAM_INT);
        
                  if(TEST) {echo '<b>'.__FILE__.' SAYS:</b><pre>';
                    echo '$sql='; print_r(
                    str_replace(':tipd', $p_tipd,
                      str_replace(':imekup','%'.$p_imekup.'%', $sql))
                    ); echo '</pre>'; 
                  }
        
        $stmt->execute();
        
        // Each call to PDOStatement::fetch() or PDOStatement::fetchAll() will 
        // update all the variables that are bound to columns. 
        $stmt->bindColumn('BROJ_IZDATNICE', $BROJ_IZDATNICE);
        $stmt->bindColumn('DATUM_FMT', $DATUM_FMT);
        $stmt->bindColumn('IME_KUPCA', $imekup);
        //$stmt->bindColumn(3, $imekup);
        $stmt->bindColumn('SIFRA_TIP_DOC', $tipd);

        $errorInfo = $stmt->errorInfo();
        if (isset($errorInfo[2])) {
            $error = $errorInfo[2];
        }
  } catch (Exception $e) {
        $error = $e->getMessage();
  }
}
$title_tab = 'tblIZDATNICA';
include($_SERVER['DOCUMENT_ROOT'].'/inc/hdr.php');
//include('z_local_hdr.php');
?>
</head>
<body>
<h1>Tablica izdatnica</h1>
<!--h1>PDO Prepared Statement: Binding Output Parameters</h1-->
<?php if (isset($error)) {
    echo "<p>$error</p>";
} 

  // ******************************************
  // View  P a r a m e t e r s  f o r m
  //       (s e a r c h  f o r m)
  // ******************************************
?>
<form method="get" action="<?php echo $_SERVER['PHP_SELF']; ?>">
    <fieldset>
      <legend>Filter redaka: utipkajte nekoliko znakova pa tipka ENTER
      ili gumb "Upit"</legend>
      <p>
        <label for="imekup">Ime kupca </label>
        <input type="text" name="imekup" id="imekup" 
               value="<?= $p_imekup ?>" autofocus  
               placeholder="Filter redaka upita...">
        
        <label for="tipd">Tip izdatnice </label>
        <select name="tipd" id="tipd" value="<?= $p_tipd ?>">
            <?php
              echo "<option value='9999'"; if ($p_tipd == '9999') echo ' selected';
              echo '>' . 'SVI TIPOVI IZDATNICA' . '</option>';
              echo "<option value='4'"; if ($p_tipd == '4') echo ' selected';
              echo '>'.'Izdatnice'.'</option>';
              echo "<option value='8'"; if ($p_tipd == '8') echo ' selected';
              echo '>'.'MP zaklučci'.'</option>';
              /*
              for ($p = 1; $p <= 11; $p+=1) {
                echo "<option value='$p'";
                if ($p == 4) { echo ' selected'; }
                echo '>' . number_format($p) . '</option>';
              } 
              */
            ?>
        </select>
        <input type="submit" name="search" value="Upit">
      </p>
      <?php if (TEST) echo 'Redosljed '.$p_sortdml; ?>
    </fieldset>
</form>



<?php
if (isset($_GET['search'])) 
{
  // ******************************************
  // View  t a b l e
  // ******************************************
  $stmt->fetch(PDO::FETCH_BOUND);
  if ($imekup) 
  {
    // http://dev1:8083/z30GB/02_test/08pdo/01dpowers/02izdatnica_tbl.php
    //    ?search=Upit&imekup&tipd=9999&sort=BROJ_IZDATNICE
    
    ?>
    <table>
      <tr>
          <th>RBR</th>
          <th><a href="<?php $p_sort = 'SIFRA_TIP_DOC';
                  echo $_SERVER['PHP_SELF']
                      .'?search=Upit'
                      .'&imekup='.$p_imekup
                      .'&tipd='.$p_tipd
                      .'&sort='.$p_sort;
              ?>">TIP</a>
          </th>
          <th><a href="<?php $p_sort = 'BROJ_IZDATNICE';
                  echo $_SERVER['PHP_SELF']
                      .'?search=Upit'
                      .'&imekup='.$p_imekup
                      .'&tipd='.$p_tipd
                      .'&sort='.$p_sort;
              ?>">BR.IZD.</a>
          </th>
          <th>DATUM</th>
          <th><a href="<?php $p_sort = 'IME_KUPCA';
                  echo $_SERVER['PHP_SELF']
                      .'?search=Upit'
                      .'&imekup='.$p_imekup
                      .'&tipd='.$p_tipd
                      .'&sort='.$p_sort;
              ?>">IME KUPCA</a>
          </th>
      </tr>
      
      
      <?php
      $ii = 1;
      do 
      { ?>
        <tr>
            <td><?= $ii++; ?></td>
            <td><?= number_format($tipd); ?></td>
            <td><b><?= $BROJ_IZDATNICE; ?></b></td>
            <td><?= $DATUM_FMT; ?></td>
            <td><?= $imekup; ?></td>
        </tr>
        <?php 
      } while ($stmt->fetch(PDO::FETCH_BOUND)); 
      ?>
      
      
    </table>
    <?php 
  } else {
       echo '<p>No results found.</p>';
    } 
} 
?>
</body>
</html>

2.Sept.2015 Download : click HOME link on this page top and see site_ver2.rar from my article 9.

I. Testing is possible with code below :
– uncomment lines // PHP PDO
– comment lines // E.Rangel’s PDOOCI and code beginning with
use chcons as cnf; (before $c1 = DB_DSN;)

E.Rangel’s PDOOCI works ok same as PHP PDO which is experimental.
PDO OCI is PDO sintax with OCI8 DBI interface.

  1. <?php
    // http://dev1:8083/test/t_oci8/undergr/pdo.php
    // H:\dev_web\htdocs\test\t_oci8\undergr\pdo.php
    
    // ******************************************
    use chcons as cnf;
    $confglob_dir = realpath($_SERVER['DOCUMENT_ROOT'].'/../inc'); 
    if (!defined('DS')) define('DS',DIRECTORY_SEPARATOR);
     //require_once($confglob_dir.DS.'confglob.php');
    // 1. v a r i a b l e s & f n s :
    require_once($confglob_dir.DS.'utl'.DS.'utls.php');
    cnf\chcons::ini(__FILE__ // $idxfle
     ,dirname(dirname(__DIR__)) . DS.'index.php'); // $aplfle=test dir
    // 2. d b c o n n p a r a m s :
    require_once(CONFGLOB_DIR.DS.'confglobcondb.php');
    // ******************************************
    
    $c1 = DB_DSN; 
    //$c1 = 'oci:dbname=sspc/XE:pooled'; 
    //$c1 = 'oci:dbname=localhost/XE:pooled'; 
    //$c1 = 'oci:dbname=127.0.0.1/XE:pooled'; 
    $c2 = SCHEMA; 
    //$c2 = 'hr'; 
    $c3 = PASSWORD;
    //$c3 = 'hr';
    $opt = array(PDOP1.' => '.PDOP1VAL) ; // PDO parameter 1
    //$opt = array(PDO::ATTR_PERSISTENT => TRUE) ;
    ?>
    
    <!DOCTYPE html>
    <!-- saved from url=(0075)http://www.oracle.com/technetwork/articles/dsl/php-web-auditing-171451.html -->
    <html>
    
    <head><meta http-equiv="Content-Type" content="text/html;
     charset=UTF-8">
    
    
    <style type="text/css">
    <!--
    /* 
     J:\awww\apl\inc\cssfmt\style01.css
     font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; 
     font-style: normal; "Trebuchet MS"
    */
    body {
     padding: 10px;
     color:black; background-color: #F0F0F0;
     font-size: 16px;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
     font-weight: normal; text-align: justify;
    }
    /* p, li {
     font-size: 16px;
    }*/
    
    p.group {color:#3366cc}
    
    p#pla {color:blue} /* <-- ZADNJI P R E G A Z I
     p r ethodne istoimene */
    
    p#lju {color:magenta}
    
    p#zel {color:green}
    
    img {
     float:right;
     margin-right:10px
    }
    
    table {
     margin: auto;
     /* background-color: #FFFFFF; */
     border-collapse: collapse;
     border-style: solid; border-color: lightblue;
     border-width: 1px 3px 3px 1px; /* T,R,D,L smedja= #936709*/
     /* border-color: #000000; border: 0; */
     margin: 5px;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    td, th {
     /* border: solid #000000 1px; */
     /* text-align: left; */
     padding: 2px;
     height: 20px;
     /* max-width: 300px; */
    }
    
    .maintable {
     border: 2px solid #376EAB;
    }
    .parameter {
     font-weight: bold; color: #6586AC;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    h1 {
     font-size: 20px;
     color: #A5663D; /* 7A7272=siva */
     font-weight: normal;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    h2 {
     font-size: 18px;
     font-weight: bold;
     color: #303030;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    h3 {
     font-size: 16px;
     font-weight: bold;
     color: #2B5885;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    div {
     margin: 5px;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    div.settings {
     margin-right: 0;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    a:link, a:visited, a:active {
     color: #294F75;
     text-decoration: none;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    a:hover {
     color: #182634;
     text-decoration: underline;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    .txtBody {
     font-size: 16px;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
     font-style: normal; font-weight: normal;
    }
    .txtBody18px {
     font-size: large; font-style: normal; font-weight: normal;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    
    
    
    .mainTitle {
     font-family: Impact;
     font-size: 24px;
    }
    .mnuMainH {
     text-align: center;
     cursor: pointer;
     cursor: hand;
     color: #294F75;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    .imgPlusMinus {
     float : left
     /* cursor: pointer;
     cursor: hand;
     height: 14px; */
    }
    .NotDisp {
     display: none;
    }
    .hdr_open_close_section {
     color: #000;
     background-color:#E8E8E8; /* CCC=svjetlosiva E8E8E8=jos vise svjetlosiva */
     font-weight: bold;
     font-family: Corbel, Constantia, Arial, 'Helvetica', sans-serif;
    }
    .pgBreakAfter {
     cursor: text;
     filter: Gray;
     page-break-after: always;
    }
    .pgBreakBefore {
     cursor: text;
     filter: Gray;
     page-break-before: always;
    }
    /* <span class="pgBreakAfter"> </span> */
    
    
    
    -->
    </style>
    
    </head>
    
    <body>
    
     <table border="0" align="center" 
     cellpadding="3" cellspacing="1">
     <tr>
     <td width="700px" colspan="1" valign="top">
     
     
     
    <?php
    
    // -------------------------------------------------
    echo '<h1>I. ORACLE 11g XE PERSISTENT DB CONNECTION POOLED 
    <br />&nbsp;&nbsp;&nbsp;(ESTABLISHED WITH PHP PDO OR E.Rangel\'s PDOOCI) </h1>';
    // -------------------------------------------------
    //$c1 = 'oci:dbname=localhost/XE:pooled'; $c2 = 'hr'; $c3 = 'hr';
    //$c2 = 'mercedes'; $c3 = 'm1';
    try {
     $dbh = new PDO($c1, $c2, $c3);
    } catch (PDOException $e) {
     echo "*****Error, naredba:<br /><br /> \$dbh = new PDO('$c1', '$c2', '$c3');"
     , '<br /><br />', $e->getMessage()
     ;
     exit;
    }
    echo '<ol>';
    echo '<li><pre>';
    echo <<< 'EOTXT'
    // ******************************************
    use chcons as cnf;
    $confglob_dir = realpath($_SERVER['DOCUMENT_ROOT'].'/../inc'); 
    if (!defined('DS')) define('DS',DIRECTORY_SEPARATOR);
     //require_once($confglob_dir.DS.'confglob.php');
     
    // 1. v a r i a b l e s & f n s :
    require_once($confglob_dir.DS.'utl'.DS.'utls.php');
    cnf\chcons::ini(__FILE__ // $idxfle
     ,dirname(dirname(__DIR__)) . DS.'index.php'); // $aplfle=test dir
    // 2. d b c o n n p a r a m s :
    
    require_once(CONFGLOB_DIR.DS.'confglobcondb.php');
    // ******************************************
    
    $c1 = DB_DSN; // E.Rangel's PDOOCI
    //$c1 = 'oci:dbname=sspc/XE:pooled'; // PHP PDO
    //$c1 = 'oci:dbname=localhost/XE:pooled'; 
    //$c1 = 'oci:dbname=127.0.0.1/XE:pooled'; 
    
    $c2 = SCHEMA; // E.Rangel's PDOOCI
    //$c2 = 'hr'; // PHP PDO
    
    $c3 = PASSWORD; // E.Rangel's PDOOCI
    //$c3 = 'hr'; // PHP PDO
    
    $opt = array(PDOP1.' => '.PDOP1VAL) ; // PDO parameter 1 // E.Rangel's PDOOCI
    //$opt = array(PDO::ATTR_PERSISTENT => TRUE) ; // PHP PDO
    EOTXT;
    echo '</pre>';
    
    
    echo '<li>';
    echo "Connected to user: ************ \$dbh = 
     <br />new PDO('$c1', '$c2', '$c3');";
    
    
    //$c1 = 'oci:dbname=localhost/XE'; //$c2 = 'mercedes'; $c3 = 'm1';
    try {
     $dbh = new PDO($c1, $c2, $c3, $opt);
    } catch (PDOException $e) {
     echo "*****Error, naredba:<br /><br /> \$dbh = new PDO('$c1', '$c2', '$c3', '\$opt');"
     , '<br /><br />', $e->getMessage()
     ;
     exit;
    } // ."\n"
    
    echo "<li> PERSISTENT Connected to user: ************\$dbh = 
     <br />new PDO('$c1', '$c2', '$c3', array(PDO::ATTR_PERSISTENT => TRUE));";
    echo '<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp $opt = array(PDO::ATTR_PERSISTENT => TRUE) = '; 
     print_r($opt); //echo '</pre>'; 
    echo '</ol>';
    
    
    
    
    
    //
    echo '<br/>';
    echo '<ol>';
    echo '<li>';
    $pdodriver = $dbh->getAttribute(PDO::ATTR_DRIVER_NAME);
    if ($pdodriver == 'mysql') {
     echo "Running on mysql; doing something mysql specific here\n";
    } else echo 'PDO driver: $dbh->getAttribute(PDO::ATTR_DRIVER_NAME) = ' 
     . $pdodriver;
    echo '<li>';
    echo ''.'not supported for OCI PDO: '
     . '$dbh->getAttribute(PDO::ATTR_CONNECTION_STATUS);';
    //echo 'Connection Status ' . $dbh->getAttribute(PDO::ATTR_CONNECTION_STATUS);
    echo '<li>';
    echo 'DB Server Version $dbh->getAttribute(PDO::ATTR_SERVER_VERSION) = <br />' 
     . $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
    echo '<li>';
    echo '$dbh->getAttribute(PDO::ATTR_SERVER_INFO) = <br />' 
     . $dbh->getAttribute(PDO::ATTR_SERVER_INFO);
    echo '<li>';
    echo 'Client Library Server Ver. $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION) = ' 
     . $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION);
    echo '</ol>';
    //
    
    $dbh->setAttribute(
     PDO::ATTR_ERRMODE, 
     PDO::ERRMODE_EXCEPTION 
    );
    
    
    
    function nextidrec($dbh, $query) {
     $sth = $dbh->prepare($query);
     $sth->execute();
     $row = $sth->fetch(PDO::FETCH_NUM);
     return $row[0] + 1;
    }
    function countrec($dbh, $query) {
     $sth = $dbh->prepare($query);
     $sth->execute(array('usr2'));
     $row = $sth->fetch(PDO::FETCH_NUM);
     return $row[0];
    }
    echo '<br/>';
    // -------------------------------------------------
    echo '<h1>II. PDO CRUD DML (QUERY)</h1>';
    // -------------------------------------------------
    //$db = new PDO("DSN");
    // -------------------------------------------------
    echo '<h3>II.1 max ID & C R E A T E with bind variables - prepare, execute, fetch</h3>';
    // -------------------------------------------------
    echo '<ol>';
    
    // 11111111111
    echo '<li>';
    $sth = $dbh->prepare("DELETE T_WEBUSER");
    $sth->execute();
     print 'DELETE T_WEBUSER table';
    
    // 22222222222
    echo '<li>';
     $countrec = countrec($dbh, "SELECT count(*) from T_WEBUSER");
     print 'SELECT count(*) from T_WEBUSER, $row = array with NUMERIC KEYS ';
     echo '<br />$countrec in table = '. $countrec; 
    
    if($countrec == 0) $nextidrec = 1;
    else {
     $nextidrec = nextidrec($dbh, "SELECT max(MEMBER_ID) from T_WEBUSER");
    }
    
    // 3333333333333
    echo '<li>';
    echo '$nextidrec (from DB records, if=1 this is first record in table - unknown sequence) = '. $nextidrec;
    
    // 444444444444
    echo '<li>';
    echo '$sth = $dbh->prepare(\'INSERT INTO T_WEBUSER 
    (UNAME, PASSWORD) 
    VALUES(?,?)\'
    ) <br />// and same for usr2, BUT NOT MEMBER_ID - TRIGGER !!'; ; 
    
    $sth = $dbh->prepare('INSERT INTO T_WEBUSER 
    (UNAME, PASSWORD) 
    VALUES(?,?)'
    ); 
    $sth->execute(array('usr1','pswusr1'));
    
    $sth = $dbh->prepare("INSERT INTO T_WEBUSER 
    (UNAME, PASSWORD) 
    VALUES(?,?)"); 
    $sth->execute(array('usr2','pswusr2'));
     
    echo '</ol>';
    
    
    
    // -------------------------------------------------
    echo '<h3>II.2 U P D A T E with bind variable</h3>';
    // -------------------------------------------------
    echo '<ol>';
    
    // 11111111111
    echo '<li>';
    echo 'UPDATE T_WEBUSER SET FORENAME = \'FORENAME\' where UNAME = ?';
     $sth = $dbh->prepare("UPDATE T_WEBUSER SET FORENAME = 'FORENAME' where UNAME = ?"); 
     $sth->execute(array('usr1'));
    
    // 2222222222222
    echo '<li>';
    echo 'All rows: SELECT * FROM T_WEBUSER';
    
    $sth = $dbh->prepare( "SELECT * FROM T_WEBUSER" );
    $sth->execute();
    
    echo "<table border='1'>\n";
    while (($row = $sth->fetch(PDO::FETCH_ASSOC)) != false) {
     echo " <tr>";
     echo " <td>".htmlentities($row['MEMBER_ID'])."</td>";
     echo " <td>".htmlentities($row['UNAME'])."</td>";
     echo " <td>".htmlentities($row['PASSWORD'])."</td>";
     echo " <td>".htmlentities($row['FORENAME'])."</td>";
     echo " </tr>";
    }
    echo "</table>\n";
     
    echo '</ol>';
    
    
    
    // -------------------------------------------------
    echo '<h3>II.3 D E L E T E with bind variable</h3>';
    // -------------------------------------------------
    echo '<ol>';
    
    // 11111111111
    echo '<li>';
    echo 'DELETE T_WEBUSER where UNAME = ?';
     $sth = $dbh->prepare('DELETE T_WEBUSER where UNAME = ?');
     $sth->execute(array('usr2'));
    
    echo '</ol>';
    
    
    // -------------------------------------------------
    echo '<h3>II.4 R E T R I V E with bind variable</h3>';
    // -------------------------------------------------
    echo '<ol>';
    
    // 11111111111
    echo '<li>';
    echo 'All rows: SELECT * FROM T_WEBUSER WHERE UNAME=? (? means bind variable value)';
    
    $sth = $dbh->prepare( "SELECT * FROM T_WEBUSER WHERE UNAME=? or UNAME=?" );
    $sth->execute(array('usr1', 'usr2'));
    
    //$row = $sth->fetch(PDO::FETCH_ASSOC);
    //echo '<pre>'; print_r($row); echo '</pre>';
    
    echo "<table border='1'>\n";
    while (($row = $sth->fetch(PDO::FETCH_ASSOC)) != false) {
     echo " <tr>";
     echo " <td>".htmlentities($row['MEMBER_ID'])."</td>";
     echo " <td>".htmlentities($row['UNAME'])."</td>";
     echo " <td>".htmlentities($row['PASSWORD'])."</td>";
     echo " <td>".htmlentities($row['FORENAME'])."</td>";
     echo " </tr>";
    }
    echo "</table>\n";
    
    // 2222222222
    echo '<li>';
    echo 'One row: SELECT * FROM T_WEBUSER WHERE UNAME=? (? means bind variable value)';
    
    $sth = $dbh->prepare( "SELECT * FROM T_WEBUSER WHERE UNAME=?" );
    $sth->execute(array('usr1'));
    $row = $sth->fetch(PDO::FETCH_ASSOC);
    echo '<pre>'; print_r($row); echo '</pre>';
    
    echo '</ol>';
    
    
    
    /*
    $dbh->exec("INSERT INTO T_WEBUSER (MEMBER_ID, UNAME, PASSWORD)
    VALUES(null,'usr1','usr1')");
    
    echo '<pre>2. $dbh->exec("UPDATE T_WEBUSER SET FORENAME = \'FORENAME\' where MEMBER_ID = 1");</pre>';
    $dbh->exec("UPDATE T_WEBUSER SET FORENAME = 'FORENAME' where MEMBER_ID = 1");
    
    
    echo "<br/>3. SELECT * FROM T_WEBUSER where MEMBER_ID in (1)<br/>";
    echo '<pre>3.1 Array Fetching
    ==============';
    $sth = $dbh->prepare("SELECT * FROM T_WEBUSER order by MEMBER_ID desc");
    $sth->execute();
    //moze i ovako: $sth = $dbh->query("SELECT * FROM T_WEBUSER order by MEMBER_ID desc");
    //while 
    ($row = $sth->fetch(PDO::FETCH_NUM));
     print '<br/>1. FETCH_NUM $row == array with NUMERIC KEYS ';
     print_r($row);
    
    //while 
    ($row = $sth->fetch(PDO::FETCH_ASSOC));
     print '3.2 FETCH_ASSOC $row == array with associated (STRING) KEYS ';
     print_r($row);
    
    //while 
    ($row = $sth->fetch(PDO::FETCH_BOTH));
     print '3.3 FETCH_BOTH row == array with ASSOCIATED & NUMERIC KEYS ';
     print_r($row);
     
    echo '<pre>2. Fetch as String - column
    ===============';
    //$sth = $dbh->prepare("SELECT opis FROM T_WEBUSER order by MEMBER_ID desc");
    //$sth->execute();
    if ($opis = $sth->fetchColumn(0) . ' i ' . $sth->fetchColumn(1)) { 
     print '$sth->fetchColumn(0) i $sth->fetchColumn(1) vrati string:<br/>';
     print_r($opis); }
    // fetch data contained within single column.
    //$u = $db->query("SELECT users WHERE login='login' AND //password='password'");
    // // fetch(PDO::FETCH_COLUMN)
    //if ($u->fetchColumn()) { // returns a string
    // // login OK
    //} else { //authentication failure 
    //}
    
    echo '<pre>3. Fetch as Standard Object
    ========================';
    //while 
    ($row = $sth->fetch(PDO::FETCH_OBJ));
     print 'FETCH_OBJ fetch row as instance of stdClass 
     where column name == property name $row->MEMBER_ID=' . $row->MEMBER_ID .'<br/>';
     print_r($row);
     
    */
    
    
    
     
    //echo '<pre>4. Fetch Into a Class
    //==================';
    // ? PDO allows the sthult to be fetched into a class type of your choice.
    //
    //$sth = $db->query("SELECT * FROM foo");
    //$sth->setFetchMode(
    // PDO::FETCH_CLASS,
    // "className",
    // array('optional'='Constructor Params')
    //);
    //while ($obj = $sth->fetch()) {
    // // $obj == instance of className
    //}
    //
    //? PDO allows query result to be used to determine destination class.
    //
    //$sth = $db->query("SELECT * FROM foo");
    //$sth->setFetchMode(
    // PDO::FETCH_CLASS |
    // PDO::FETCH_CLASSTYPE
    //);
    //while ($obj = $sth->fetch()) {
    // // $obj == instance of class who's name is
    // // found in the value of the 1 st column
    //}
    
    
    
    
    //echo '<pre>5. Fetch Into an Object
    //====================';
    //
    //? PDO even allows retrieval of data into existing object.
    //
    //$u = new userObject;
    //$sth = $db->query("SELECT * FROM users");
    //$sth->setFetchMode(PDO::FETCH_INTO, $u); 
    //while ($sth->fetch()) {
    // // will re-populate $u with row values
    //}
    //
    //echo '<pre>6. Result Iteration
    //=================';
    //
    //? PDOStatement implements Iterator interface, which allows for 
    // a method-less result iteration. 
    // 
    //$sth = $db->query(
    // "SELECT * FROM users", 
    // PDO::FETCH_ASSOC
    //);
    //foreach ($sth as $row) {
    // // $row == associated array representing row's values.
    //}
    //
    //
    //
    //
    //echo '<pre>7. Lazy Fetching
    //=============';
    //
    //? Lazy fetches returns a result in a form object, 
    // but HOLDS OF POPULATING PROPERTIES UNTIL THEY ARE ACTUALLY USED. 
    //$sth = $db->query(
    // "SELECT * FROM users", 
    // PDO::FETCH_LAZY
    //);
    //foreach ($sth as $row) {
    // echo $row['name']; // only fetch name column
    //}
    //
    
    
    
    //echo '<pre>8. fetchAll()
    //===========';
    //? retrieval of all results from query right away (handy for templates) 
    //? Can be memory intensive for large results sets!
    //
    //$qry = "SELECT * FROM users";
    //$sth = $db->query($qry)->fetchAll(PDO::FETCH_ASSOC);
     // $sth == array of all result rows, where each row is an associated array.
    
    //echo '<pre>9. Callback Function
    //==================';
    //
    //? fetch mode where each result is processed via a callback function. 
    //
    //function draw_message($subject,$email) { _ }
    //
    //$sth = $db->query("SELECT * FROM msg");
    //$sth->fetchAll(PDO::FETCH_FUNC, "draw_message");
    
    
    
    /*
    echo '<pre>$dbh->exec("DELETE T_WEBUSER where MEMBER_ID in(1,2)");</pre>';
    $dbh->exec("DELETE T_WEBUSER where MEMBER_ID in(1,2)");
    */
    
    
    echo '<br/>'.'<br/>'.'<br/>'.'<br/>';
    echo <<<'EOTXT'
    <pre>
    
    
    </pre>
    EOTXT;
    ?>
    <h1>III. PHP Data Objects Layer (PDO) - Ilia Alshanetsky</h1>
    <p> Written in C - FAST! Designed to make use of all PHP 5.1 features </p>
    <p>What Databases are Supported?<br>
     ? At time ~ PHP 5.1 PDO offered following drivers:<br>
     ? MySQL 3,4,5 (depends on client libs) <br>
     ? PostgreSQL<br>
     ? SQLite 2 &amp; 3 <br>
     ? ODBC<br>
     ? DB2<br>
     <strong>? Oracle - 2015 year still experimental</strong><br>
     ? Firebird<br>
     ? FreeTDS/Sybase/MSSQL</p>
    <h2>Installing PDO</h2>
    <p> ? PDO is divided into two components <br>
     ? CORE (provides the interface) <br>
     ? DRIVERS (access to particular database) - Example: pdo_mysql<br>
     ? The CORE is enabled by default, <br>
     drivers with the exception of pdo_sqlite are not. <br>
     Actual Install Steps :<br>
     ? PECL Way<br>
     ? pecl install pdo_[driver_name] <br>
     ? Update php.ini and add<br>
     extension=pdo_[driver_name].so (or .dll on win32) <br>
     ? Built into PHP Built<br>
     ? ./configure -with-pdo-[driver_name]<br>
     ? For Win32 dlls for each driver are available.</p>
    <h2>Using PDO - connection<br>
    </h2>
    <p><strong>// Oracle <br>
     $dbh = 
     new PDO('oci:dbname=sspc/XE:pooled', 'hr', 'hr', array(PDO::ATTR_PERSISTENT =&gt; TRUE));</strong></p>
    <p> // MySQL <br>
     new PDO('mysql:host=localhost;dbname=testdb', $login, $passwd);</p>
    <p>// PostgreSQL<br>
     new PDO('pgsql:host=localhost port=5432 dbname=testdb user=john password=mypass');</p>
    <p>// SQLite<br>
     new PDO('sqlite:/path/to/database_file');</p>
    <h2>What if the Connection Fails?</h2>
    <p> ? As is the case with most native PHP objects, <br>
     instantiation failure lead to an exception being thrown.<br>
     try {<br>
     $db = new PDO(_);<br>
     } catch (PDOException $e) {<br>
     echo $e-&gt;getMessage();<br>
     }<br>
    </p>
    <h2>Persistent Connections</h2>
    <p> ? Connecting to complex DB like Oracle is a slow process, <br>
     it would be nice to re-use a previously opened connection.</p>
    <p>$opt = array(PDO::ATTR_PERSISTENT =&gt; TRUE) ;<br>
     try {<br>
     $db = new PDO(&quot;dsn&quot;, $l, $p, $opt);<br>
     } catch (PDOException $e) {<br>
     echo $e-&gt;getMessage();<br>
     }</p>
    <h2>DSN INI Tricks<br>
    </h2>
    <p> ? The DSN string can be an INI setting <br>
     you can &quot;name&quot; as many DSNs are you like.<br>
     <br>
     ini_set(&quot;pdo.dsn.ilia&quot;, &quot;sqlite::memory&quot;);<br>
     try {<br>
     $db = new PDO(&quot;ilia&quot;);<br>
     } catch (PDOException $e) {<br>
     echo $e-&gt;getMessage();<br>
     }<br>
    </p>
    <h1>Queries</h1>
    <p>? Query execution in PDO can be done in two ways :</p>
    <p><strong>? Prepared Statements (recommended for speed &amp; security) - bind variables</strong><br>
     ? Direct Query Execution</p>
    <h1>Prepared Statements - prepare, execute, fetch</h1>
    <p>? Compile once, execute as many times as you want.<br>
     ? Clear SEPARATION BETWEEN STRUCTURE AND INPUT, which prevents SQL injection. <br>
     ? Often faster then query()/exec() even for single runs.</p>
    <p>$stmt = $db-&gt;prepare( &quot;SELECT * FROM users WHERE id=?&quot; );<br>
     $stmt-&gt;execute(array($_GET['id']));<br>
     $stmt-&gt;fetch(PDO::FETCH_ASSOC);</p>
    <p>Bound Parameters<br>
     ================<br>
     ? Prepared statements parameters can be given names and bound to variables. </p>
    <p>$stmt = $db-&gt;prepare(&quot;INSERT INTO users VALUES(:name,:pass,:mail)&quot;);<br>
     foreach (array('name','pass','mail') as $v)<br>
     $stmt-&gt;bindParam(':'.$v, $$v);<br>
     $fp = fopen(&quot;./users&quot;, &quot;r&quot;);<br>
     while (list($name,$pass,$mail) = fgetcsv($fp,4096)) {<br>
     $stmt-&gt;execute();<br>
     }</p>
    <p>Bound Result Columns<br>
     ====================<br>
     ? Result columns can be bound to variables as well.</p>
    <p>$qry = &quot;SELECT :type, :data FROM images LIMIT 1&quot;;<br>
     $stmt = $db-&gt;prepare($qry);<br>
     $stmt-&gt;bindColumn(':type',$type);<br>
     $stmt-&gt;bindColumn(':type',STDOUT,PDO::PARAM_LOB);<br>
     $stmt-&gt;execute(PDO::FETCH_BOUND);<br>
     header(&quot;Content-Type: &quot;.$type);</p>
    <p>Partial Data Retrieval<br>
     ======================<br>
     ? you only want part of the data on the cursor. <br>
     To properly end the cursor use closeCursor() method. <br>
     <br>
     $res = $db-&gt;query(&quot;SELECT * FROM users&quot;);<br>
     foreach ($res as $v) {<br>
     if ($res['name'] == 'end') {<br>
     $res-&gt;closeCursor();<br>
     break;<br>
     }<br>
     }</p>
    <p>Transactions<br>
     ============<br>
     ? Nearly all PDO drivers talk with transactional DBs, so PDO provides <br>
     handy methods for this purpose.</p>
    <p>$db-&gt;beginTransaction();<br>
     if ($db-&gt;exec($qry) === FALSE) { $db-&gt;rollback(); }<br>
     $db-&gt;commit();</p>
    <p>Metadata<br>
     =========<br>
     ? Like most native db interfaces PDO can access query metadata.</p>
    <p>$res = $db-&gt;query($qry);<br>
     $ncols = $res-&gt;columnCount();<br>
     for ($i=0; $i &lt; $ncols; $i++) {<br>
     $meta_data = $stmt-&gt;getColumnMeta($i);<br>
     }</p>
    <p>getColumnMeta() Result<br>
     ======================<br>
     ? native_type - PHP data type<br>
     ? driver:decl_type - data type of the column according to DB.<br>
     ? flags - will return any flags particular to this column in array.<br>
     ? name - of the column as returned by DB without any normalization.<br>
     ? len - maximum length of a string column, may not always be available,<br>
     will be set to -1 if it isn't.<br>
     ? precision - numeric precision of this column. <br>
     ? pdo_type - column type according to PDO as one of <br>
     pdo_type PDO_PARAM constants.</p>
    <p>lastInsertId()<br>
     ==============<br>
     ? Many db have unique identifier assigned to each newly inserted row. <br>
     PDO provides to each newly inserted row. access to this value <br>
     via lastInsertId() method.<br>
     ? Can take optional sequence name as parameter.<br>
     ? Useful for PostgreSQL</p>
    <p>if ($db-&gt;exec(&quot;INSERT INTO _&quot;)) {<br>
     $id = $db-&gt;lastInsertId();<br>
     }</p>
    <p>Connection Information<br>
     ======================<br>
     ? Some connection information can be obtained via <strong>getAttribute() 
     PDO method</strong>. </p>
    <p>$db-&gt;getAttribute(PDO::ATTR_SERVER_VERSION);<br>
     &nbsp;&nbsp;&nbsp; // Database Server Version</p>
    <p> $db-&gt;getAttribute(PDO::ATTR_CLIENT_VERSION);<br>
     &nbsp;&nbsp;&nbsp;&nbsp; // Client Library Server Version</p>
    <p> $db-&gt;getAttribute(PDO::ATTR_SERVER_INFO);<br>
     &nbsp;&nbsp;&nbsp;&nbsp; // Misc Server information</p>
    <p> $db-&gt;getAttribute(PDO::ATTR_CONNECTION_STATUS);<br>
     &nbsp;&nbsp;&nbsp;&nbsp; // Connection Status</p>
    <p>Extending PDO <br>
     =============<br>
     class DB extends PDO {<br>
     function query($qry, $mode=NULL) {<br>
     &nbsp;&nbsp;&nbsp;&nbsp; $res = parent::query($qry, $mode);<br>
     &nbsp;&nbsp;&nbsp;&nbsp; if (!$res) {<br>
     &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; var_dump($qry, $this-&gt;errorInfo());<br>
     &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return null;<br>
     &nbsp; &nbsp;&nbsp; } else { return $res; }<br>
     }<br>
     }<br>
    </p>
    <p>&nbsp;</p>
    <h1>Direct Query Execution - exec - standard: do not use</h1>
    <p> ? Queries that modify information need to be run via exec() method.</p>
    <p>? RETURN VALUE IS NUMBER OF ROWS AFFECTED BY OPERATION OR FALSE ON ERROR. </p>
    <p>$db = new PDO(&quot;DSN&quot;);<br>
     $db-&gt;exec(&quot;INSERT INTO foo (id) VALUES('bar')&quot;);<br>
     $db-&gt;exec(&quot;UPDATE foo SET id='bar'&quot;);</p>
    <p>? In some cases &quot;change&quot; queries may not affect any rows and return 0,<br>
     so TYPE-SENSITIVE COMPARE TO AVOID FALSE POSITIVES! </p>
    <p>$res = $db-&gt;exec(&quot;UPDATE foo SET id='bar'&quot;);</p>
    <p>if (!$res) // Wrong<br>
     <strong>if ($res !== FALSE) // Correct</strong></p>
    <p>Retrieving Error Information<br>
     =======================<br>
     ? PDO Provides 2 methods of getting error info:</p>
    <p>? errorCode() - SQLSTATE <br>
     ? Example:&nbsp; 42000 == Syntax Error </p>
    <p>? errorInfo() - Detailed error information <br>
     ? Ex. array( <br>
     [0] =&gt; 42000,<br>
     [1] =&gt; 1064<br>
     [2] =&gt; You have an error in your SQL syntax; <br>
     ))</p>
    <p>Better Error Handling<br>
     =====================<br>
     ? Being an OO extension PDO would allow error handling via Exceptions.<br>
     ? Any query failure will throw an Exception. </p>
    <p>$db-&gt;setAttribute(<br>
     PDO::ATTR_ERRMODE, <br>
     PDO::ERRMODE_EXCEPTION <br>
     );</p>
    <p>? When executing queries that retrieve query() method <br>
     needs to be used. <br>
     ? On error FALSE is returned </p>
    <p>$res = $db-&gt;query(&quot;SELECT * FROM foo&quot;);<br>
     // $res == PDOStatement Object</p>
    <p>Fetch Query Results<br>
     ====================<br>
     ? One of the biggest features of PDO is its flexibility <br>
     in how data is to be fetched. <br>
     ? Array (Numeric or Associated Indexes)<br>
     ? Strings (for single column result sets) <br>
     ? Objects: stdClass, <br>
     of given class <br>
     or into an existing object<br>
     ? Callback function<br>
     ? Lazy fetching<br>
     ? Iterators<br>
     ? And more!</p>
    <p>Array Fetching<br>
     ==============</p>
    <p>$res = $db-&gt;query(&quot;SELECT * FROM foo&quot;);</p>
    <p>while ($row = $res-&gt;fetch(PDO::FETCH_NUM)){<br>
     // $row == array with NUMERIC KEYS<br>
     }<br>
     while ($row = $res-&gt;fetch(PDO::FETCH_ASSOC)){<br>
     // $row == array with associated (STRING) KEYS<br>
     }<br>
     while ($row = $res-&gt;fetch(PDO::FETCH_BOTH)){<br>
     // $row == array with ASSOCIATED &amp; NUMERIC KEYS<br>
     }</p>
    <p>Fetch as String<br>
     ===============<br>
     ? fetch data contained within single column.</p>
    <p>$u = $db-&gt;query(&quot;SELECT users WHERE login='login' AND password='password'&quot;);<br>
     // fetch(PDO::FETCH_COLUMN)<br>
     if ($u-&gt;fetchColumn()) { // returns a string<br>
     // login OK<br>
     } else { /* authentication failure */ }</p>
    <p>Fetch as Standard Object<br>
     ========================<br>
     ? You can fetch a row as an instance of stdClass <br>
     where column name == property name. <br>
     <br>
     $res = $db-&gt;query(&quot;SELECT * FROM foo&quot;);<br>
     while ($obj = $res-&gt;fetch(PDO::FETCH_OBJ)) {<br>
     // $obj == instance of stdClass<br>
     }</p>
    <p>Fetch Into a Class<br>
     ==================<br>
     ? PDO allows the result to be fetched into a class type of your choice.</p>
    <p>$res = $db-&gt;query(&quot;SELECT * FROM foo&quot;);<br>
     $res-&gt;setFetchMode(<br>
     PDO::FETCH_CLASS,<br>
     &quot;className&quot;,<br>
     array('optional'='Constructor Params')<br>
     );<br>
     while ($obj = $res-&gt;fetch()) {<br>
     // $obj == instance of className<br>
     }</p>
    <p>Fetch Into a Class Cont.<br>
     =========================<br>
     ? PDO allows query result to be used to determine destination class.</p>
    <p>$res = $db-&gt;query(&quot;SELECT * FROM foo&quot;);<br>
     $res-&gt;setFetchMode(<br>
     PDO::FETCH_CLASS |<br>
     PDO::FETCH_CLASSTYPE<br>
     );<br>
     while ($obj = $res-&gt;fetch()) {<br>
     // $obj == instance of class who's name is<br>
     // found in the value of the 1 st column<br>
     }</p>
    <p>Fetch Into an Object<br>
     ====================<br>
     ? PDO even allows retrieval of data into existing object.</p>
    <p>$u = new userObject;<br>
     $res = $db-&gt;query(&quot;SELECT * FROM users&quot;);<br>
     $res-&gt;setFetchMode(PDO::FETCH_INTO, $u); <br>
     while ($res-&gt;fetch()) {<br>
     // will re-populate $u with row values<br>
     }<br>
    </p>
    <p>Result Iteration<br>
     =================<br>
     ? PDOStatement implements Iterator interface, which allows for <br>
     a method-less result iteration. <br>
     <br>
     $res = $db-&gt;query(<br>
     &quot;SELECT * FROM users&quot;, <br>
     PDO::FETCH_ASSOC<br>
     );<br>
     foreach ($res as $row) {<br>
     // $row == associated array representing row's values.<br>
     }</p>
    <p>Lazy Fetching<br>
     =============<br>
     ? Lazy fetches returns a result in a form object, <br>
     but HOLDS OF POPULATING PROPERTIES UNTIL THEY ARE ACTUALLY USED. <br>
     $res = $db-&gt;query(<br>
     &quot;SELECT * FROM users&quot;, <br>
     PDO::FETCH_LAZY<br>
     );<br>
     foreach ($res as $row) {<br>
     echo $row['name']; // only fetch name column<br>
     }</p>
    <p>fetchAll()<br>
     ===========<br>
     ? retrieval of all results from query right away (handy for templates) <br>
     ? Can be memory intensive for large results sets!</p>
    <p>$qry = &quot;SELECT * FROM users&quot;;<br>
     $res = $db-&gt;query($qry)-&gt;fetchAll(PDO::FETCH_ASSOC);<br>
     // $res == array of all result rows, where each row is an associated array.</p>
    <p>Callback Function<br>
     ==================<br>
     ? fetch mode where each result is processed via a callback function. </p>
    <p>function draw_message($subject,$email) { _ }</p>
    <p>$res = $db-&gt;query(&quot;SELECT * FROM msg&quot;);<br>
     $res-&gt;fetchAll(PDO::FETCH_FUNC, &quot;draw_message&quot;);</p>
    <p>Direct Query Problems<br>
     =====================<br>
     ? Query interpreted on each execution - slow FREQUENTLY REPEATED queries.<br>
     ? Security issues, un-escaped user input can contain special elements<br>
     leading to SQL injection.</p>
    <p>Escaping in PDO<br>
     ===============<br>
     ? Escaping of special characters is handled via the quote() method. </p>
    <p>$qry = &quot;SELECT * FROM users <br>
     WHERE login=&quot;.$db-&gt;quote($_POST['login']).&quot;<br>
     AND passwd=&quot;.$db-&gt;quote($_POST['pass'])<br>
     ;</p></td>
    </tr>
     <tr>
     <td colspan="1" valign="top">&nbsp;</td>
     </tr>
     </table>
    </body>
    
    </html>
  2. Connected to user: ************ $dbh =
    new PDO(‘oci:host=sspc/XE:pooled;charset=EE8MSWIN1250’, ‘hr’, ‘hr’);
  3. PERSISTENT Connected to user: ************$dbh =
    new PDO(‘oci:host=sspc/XE:pooled;charset=EE8MSWIN1250’, ‘hr’, ‘hr’, array(PDO::ATTR_PERSISTENT => TRUE));
    $opt = array(PDO::ATTR_PERSISTENT => TRUE) = Array ( [0] => 0 )
  1. PDO driver: $dbh->getAttribute(PDO::ATTR_DRIVER_NAME) = oci
  2. not supported for OCI PDO: $dbh->getAttribute(PDO::ATTR_CONNECTION_STATUS);
  3. DB Server Version $dbh->getAttribute(PDO::ATTR_SERVER_VERSION) =
    11.2.0.2.0
  4. $dbh->getAttribute(PDO::ATTR_SERVER_INFO) =
    Oracle Database 11g Express Edition Release 11.2.0.2.0 – 64bit Production
  5. Client Library Server Ver. $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION) = 11.2.0.2.0

II. PDO CRUD DML (QUERY)

II.1 max ID & C R E A T E with bind variables – prepare, execute, fetch

  1. DELETE T_WEBUSER table
  2. SELECT count(*) from T_WEBUSER, $row = array with NUMERIC KEYS
    $countrec in table =
  3. $nextidrec (from DB records, if=1 this is first record in table – unknown sequence) = 1
  4. $sth = $dbh->prepare(‘INSERT INTO T_WEBUSER (UNAME, PASSWORD) VALUES(?,?)’ )
    // and same for usr2, BUT NOT MEMBER_ID – TRIGGER !!

II.2 U P D A T E with bind variable

  1. UPDATE T_WEBUSER SET FORENAME = ‘FORENAME’ where UNAME = ?
  2. All rows: SELECT * FROM T_WEBUSER
    142 usr1 pswusr1 FORENAME
    143 usr2 pswusr2

II.3 D E L E T E with bind variable

  1. DELETE T_WEBUSER where UNAME = ?

II.4 R E T R I V E with bind variable

  1. All rows: SELECT * FROM T_WEBUSER WHERE UNAME=? (? means bind variable value)
    142 usr1 pswusr1 FORENAME
  2. One row: SELECT * FROM T_WEBUSER WHERE UNAME=? (? means bind variable value)
    Array
    (
        [MEMBER_ID] => 142
        [IS_ADMIN] => 0
        [ACTIVATION_TOKEN] => 0
        [FORENAME] => FORENAME
        [LASTNAME] => 
        [UNAME] => usr1
        [EMAIL] => 
        [PASSWORD] => pswusr1
        [ACTIVE] => 
        [DATE_REGISTER] => 
        [DATE_MOD] => 
        [DATE_EXPIRES] => 
        [MEMBER_LEVEL] => 
        [TIP] => 
        [DESCRIPTION1] => 
        [DESCRIPTION2] => 
        [DESCRIPTION3] => 
    )
    
 

III. PHP Data Objects Layer (PDO) – Ilia Alshanetsky

Written in C – FAST! Designed to make use of all PHP 5.1 features

What Databases are Supported?
? At time ~ PHP 5.1 PDO offered following drivers:
? MySQL 3,4,5 (depends on client libs)
? PostgreSQL
? SQLite 2 & 3
? ODBC
? DB2
? Oracle – 2015 year still experimental
? Firebird
? FreeTDS/Sybase/MSSQL

Installing PDO

? PDO is divided into two components
? CORE (provides the interface)
? DRIVERS (access to particular database) – Example: pdo_mysql
? The CORE is enabled by default,
drivers with the exception of pdo_sqlite are not.
Actual Install Steps :
? PECL Way
? pecl install pdo_[driver_name]
? Update php.ini and add
extension=pdo_[driver_name].so (or .dll on win32)
? Built into PHP Built
? ./configure -with-pdo-[driver_name]
? For Win32 dlls for each driver are available.

Using PDO – connection

// Oracle
$dbh =  new PDO(‘oci:dbname=sspc/XE:pooled’, ‘hr’, ‘hr’, array(PDO::ATTR_PERSISTENT => TRUE));

// MySQL
new PDO(‘mysql:host=localhost;dbname=testdb’, $login, $passwd);

// PostgreSQL
new PDO(‘pgsql:host=localhost port=5432 dbname=testdb user=john password=mypass’);

// SQLite
new PDO(‘sqlite:/path/to/database_file’);

What if the Connection Fails?

? As is the case with most native PHP objects,
instantiation failure lead to an exception being thrown.
try {
$db = new PDO(_);
} catch (PDOException $e) {
echo $e->getMessage();
}

Persistent Connections

? Connecting to complex DB like Oracle is a slow process,
it would be nice to re-use a previously opened connection.

$opt = array(PDO::ATTR_PERSISTENT => TRUE) ;
try {
$db = new PDO(“dsn”, $l, $p, $opt);
} catch (PDOException $e) {
echo $e->getMessage();
}

DSN INI Tricks

? The DSN string can be an INI setting
you can “name” as many DSNs are you like.

ini_set(“pdo.dsn.ilia”, “sqlite::memory”);
try {
$db = new PDO(“ilia”);
} catch (PDOException $e) {
echo $e->getMessage();
}

Queries

? Query execution in PDO can be done in two ways :

? Prepared Statements (recommended for speed & security) – bind variables
? Direct Query Execution

Prepared Statements – prepare, execute, fetch

? Compile once, execute as many times as you want.
? Clear SEPARATION BETWEEN STRUCTURE AND INPUT, which prevents SQL injection.
? Often faster then query()/exec() even for single runs.

$stmt = $db->prepare( “SELECT * FROM users WHERE id=?” );
$stmt->execute(array($_GET[‘id’]));
$stmt->fetch(PDO::FETCH_ASSOC);

Bound Parameters
================
? Prepared statements parameters can be given names and bound to variables.

$stmt = $db->prepare(“INSERT INTO users VALUES(:name,:pass,:mail)”);
foreach (array(‘name’,’pass’,’mail’) as $v)
$stmt->bindParam(‘:’.$v, $$v);
$fp = fopen(“./users”, “r”);
while (list($name,$pass,$mail) = fgetcsv($fp,4096)) {
$stmt->execute();
}

Bound Result Columns
====================
? Result columns can be bound to variables as well.

$qry = “SELECT :type, :data FROM images LIMIT 1”;
$stmt = $db->prepare($qry);
$stmt->bindColumn(‘:type’,$type);
$stmt->bindColumn(‘:type’,STDOUT,PDO::PARAM_LOB);
$stmt->execute(PDO::FETCH_BOUND);
header(“Content-Type: “.$type);

Partial Data Retrieval
======================
? you only want part of the data on the cursor.
To properly end the cursor use closeCursor() method.

$res = $db->query(“SELECT * FROM users”);
foreach ($res as $v) {
if ($res[‘name’] == ‘end’) {
$res->closeCursor();
break;
}
}

Transactions
============
? Nearly all PDO drivers talk with transactional DBs, so PDO provides
handy methods for this purpose.

$db->beginTransaction();
if ($db->exec($qry) === FALSE) { $db->rollback(); }
$db->commit();

Metadata
=========
? Like most native db interfaces PDO can access query metadata.

$res = $db->query($qry);
$ncols = $res->columnCount();
for ($i=0; $i < $ncols; $i++) {
$meta_data = $stmt->getColumnMeta($i);
}

getColumnMeta() Result
======================
? native_type – PHP data type
? driver:decl_type – data type of the column according to DB.
? flags – will return any flags particular to this column in array.
? name – of the column as returned by DB without any normalization.
? len – maximum length of a string column, may not always be available,
will be set to -1 if it isn’t.
? precision – numeric precision of this column.
? pdo_type – column type according to PDO as one of
pdo_type PDO_PARAM constants.

lastInsertId()
==============
? Many db have unique identifier assigned to each newly inserted row.
PDO provides to each newly inserted row. access to this value
via lastInsertId() method.
? Can take optional sequence name as parameter.
? Useful for PostgreSQL

if ($db->exec(“INSERT INTO _”)) {
$id = $db->lastInsertId();
}

Connection Information
======================
? Some connection information can be obtained via getAttribute() PDO method.

$db->getAttribute(PDO::ATTR_SERVER_VERSION);
// Database Server Version

$db->getAttribute(PDO::ATTR_CLIENT_VERSION);
// Client Library Server Version

$db->getAttribute(PDO::ATTR_SERVER_INFO);
// Misc Server information

$db->getAttribute(PDO::ATTR_CONNECTION_STATUS);
// Connection Status

Extending PDO
=============
class DB extends PDO {
function query($qry, $mode=NULL) {
$res = parent::query($qry, $mode);
if (!$res) {
var_dump($qry, $this->errorInfo());
return null;
} else { return $res; }
}
}

 

Direct Query Execution – exec – standard: do not use

? Queries that modify information need to be run via exec() method.

? RETURN VALUE IS NUMBER OF ROWS AFFECTED BY OPERATION OR FALSE ON ERROR.

$db = new PDO(“DSN”);
$db->exec(“INSERT INTO foo (id) VALUES(‘bar’)”);
$db->exec(“UPDATE foo SET id=’bar'”);

? In some cases “change” queries may not affect any rows and return 0,
so TYPE-SENSITIVE COMPARE TO AVOID FALSE POSITIVES!

$res = $db->exec(“UPDATE foo SET id=’bar'”);

if (!$res) // Wrong
if ($res !== FALSE) // Correct

Retrieving Error Information
=======================
? PDO Provides 2 methods of getting error info:

? errorCode() – SQLSTATE
? Example:  42000 == Syntax Error

? errorInfo() – Detailed error information
? Ex. array(
[0] => 42000,
[1] => 1064
[2] => You have an error in your SQL syntax;
))

Better Error Handling
=====================
? Being an OO extension PDO would allow error handling via Exceptions.
? Any query failure will throw an Exception.

$db->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);

? When executing queries that retrieve query() method
needs to be used.
? On error FALSE is returned

$res = $db->query(“SELECT * FROM foo”);
// $res == PDOStatement Object

Fetch Query Results
====================
? One of the biggest features of PDO is its flexibility
in how data is to be fetched.
? Array (Numeric or Associated Indexes)
? Strings (for single column result sets)
? Objects: stdClass,
of given class
or into an existing object
? Callback function
? Lazy fetching
? Iterators
? And more!

Array Fetching
==============

$res = $db->query(“SELECT * FROM foo”);

while ($row = $res->fetch(PDO::FETCH_NUM)){
// $row == array with NUMERIC KEYS
}
while ($row = $res->fetch(PDO::FETCH_ASSOC)){
// $row == array with associated (STRING) KEYS
}
while ($row = $res->fetch(PDO::FETCH_BOTH)){
// $row == array with ASSOCIATED & NUMERIC KEYS
}

Fetch as String
===============
? fetch data contained within single column.

$u = $db->query(“SELECT users WHERE login=’login’ AND password=’password'”);
// fetch(PDO::FETCH_COLUMN)
if ($u->fetchColumn()) { // returns a string
// login OK
} else { /* authentication failure */ }

Fetch as Standard Object
========================
? You can fetch a row as an instance of stdClass
where column name == property name.

$res = $db->query(“SELECT * FROM foo”);
while ($obj = $res->fetch(PDO::FETCH_OBJ)) {
// $obj == instance of stdClass
}

Fetch Into a Class
==================
? PDO allows the result to be fetched into a class type of your choice.

$res = $db->query(“SELECT * FROM foo”);
$res->setFetchMode(
PDO::FETCH_CLASS,
“className”,
array(‘optional’=’Constructor Params’)
);
while ($obj = $res->fetch()) {
// $obj == instance of className
}

Fetch Into a Class Cont.
=========================
? PDO allows query result to be used to determine destination class.

$res = $db->query(“SELECT * FROM foo”);
$res->setFetchMode(
PDO::FETCH_CLASS |
PDO::FETCH_CLASSTYPE
);
while ($obj = $res->fetch()) {
// $obj == instance of class who’s name is
// found in the value of the 1 st column
}

Fetch Into an Object
====================
? PDO even allows retrieval of data into existing object.

$u = new userObject;
$res = $db->query(“SELECT * FROM users”);
$res->setFetchMode(PDO::FETCH_INTO, $u);
while ($res->fetch()) {
// will re-populate $u with row values
}

Result Iteration
=================
? PDOStatement implements Iterator interface, which allows for
a method-less result iteration.

$res = $db->query(
“SELECT * FROM users”,
PDO::FETCH_ASSOC
);
foreach ($res as $row) {
// $row == associated array representing row’s values.
}

Lazy Fetching
=============
? Lazy fetches returns a result in a form object,
but HOLDS OF POPULATING PROPERTIES UNTIL THEY ARE ACTUALLY USED.
$res = $db->query(
“SELECT * FROM users”,
PDO::FETCH_LAZY
);
foreach ($res as $row) {
echo $row[‘name’]; // only fetch name column
}

fetchAll()
===========
? retrieval of all results from query right away (handy for templates)
? Can be memory intensive for large results sets!

$qry = “SELECT * FROM users”;
$res = $db->query($qry)->fetchAll(PDO::FETCH_ASSOC);
// $res == array of all result rows, where each row is an associated array.

Callback Function
==================
? fetch mode where each result is processed via a callback function.

function draw_message($subject,$email) { _ }

$res = $db->query(“SELECT * FROM msg”);
$res->fetchAll(PDO::FETCH_FUNC, “draw_message”);

Direct Query Problems
=====================
? Query interpreted on each execution – slow FREQUENTLY REPEATED queries.
? Security issues, un-escaped user input can contain special elements
leading to SQL injection.

Escaping in PDO
===============
? Escaping of special characters is handled via the quote() method.

$qry = “SELECT * FROM users
WHERE login=”.$db->quote($_POST[‘login’]).”
AND passwd=”.$db->quote($_POST[‘pass’])
;

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
    */
    }

3. Zwamp server development ibrowser menu

HOME     2.April.2016 see https://github.com/slavkoss/fwphp
to download learning php code for :

Own php MVC framework (main menu) skeleton primary for learnig or using in applications.

Tested on: Windows 10 64 bit PHP 5.5.30 (min. 5.4), portable ZWAMP server menu SW (I briefly tried 4-5 others but ZWAMP with unpacking ZIPs & little config works best for me !! Others portable are “portable”.

Newest WAMP 3.0.4 seems excellent but I do not know how to use Composer with it. XAMPP is also not clear enough to me. (Same as others).

fwphp properties:

  1. newest php programming techniques, excellent learning start for Laravel, Falcon, yii2, codeigniter…
    or some micro php fw but also to build upon own main menus.
  2. MVC code organization
  3. classes grouped into namespaces with autoloading (on demand loading)
  4. advanced router with pretty URLs (Apache mod_rewrite)
  5. keys in routing tbl are regular expressions not strings
    Comming soon :
  6. controllers (programs that decide the response they are going to return as views to users)
    with action filters (useful for authentication) – partially finished
    Planned :
  7. views (presentation code) including template Twig engine (easier views coding)
  8. models (data handling code, DBInterface code) with on-demand DB connecting (resoutce friendly database connectivity)
  9. possible environment switchings (environment specific config & error handling)

All my learning/testing php code :
my blog http://phporacle.altervista.org/
or github https://github.com/slavkoss/fwphp/

2.Sept.2015 Download – see site_ver2.rar from my article 9. Here is important lsweb.php – excellent for development (and very simple once you understand it).

zwamp.rar – this is old code, use it only to see how to work with static class elements. Folder names in .rar are simmilar to my development site folder names. (All is tested with  ~newest 64 bit SW). Contains scripts to learn higher level begining PHP :

  1. Apache & php config scripts
  2. zwamp scripts – main development menu
  3. ls scripts – web server dirs navigation
  4. yiiinfo scripts

Goal is basic web development site. Best way to learn is start with small portion of code and add few lines step by step.

May 2015  Code refactored 3 :  Not finished yet, later I shall write new article “Web development and production site (virtual hosts, aliases) on Windows 8.1 , Oracle 11gXE (and 11g), PHP (all 64 bit) on hard disk and on Usb stick“. Code refactoring 3 consists of:

  1. simplify config class with static variables
  2. simplify ls scripts for web server dirs navigation
  3. simplify DBI – I tested PDOOCI DBI (Eustáquio Rangel http://github.com/taq/pdooci) –works.  I think PDOOCI is usefull becouse it is PDO code sintax layer on OCI8 DBI code layer. One day, when PHP PDO will no more be experimental PDOOCI will not be needed.
    Eustáquio made also PHPreports (also on Github)

April. 2015 Code refactored 2 :

  1. config file defines constants and contains config class with static variables (visible in all scripts, can be changed). This is last large refactoting.
  2. Added top menu – I suppose best and simplest.
  3. explanations in this page for now are not updated but code is in .rar at top of this page.
  4. Now this scripts are : OOP, SPA, MVC domain-style, PHP outside web doc root, (paths & URLs) set up with constants and static class variables.

Feb. 2015 Code refactored 1 – php outside web doc root:
Intro explanations for now not updated. Now this scripts are :
OOP, SPA, MVC domain-style, PHP outside web doc root.

10 scripts – It is not easy – same as yii2 requirements scripts. Both examples are excellent for learning PHP.

  1. Installing Apache, PHP, Oracle 11XE (all 64 bit)
  2. 11g XE on Win 8.1.1, both 64bit – APEX VERSION 4.2.5,  Maj 24, 2014
  3. Zwamp server development ibrowser menu – all on Win 8.1.1, 64bi
  4. How to recognize mobile device – oop, spa, mvc domain-style, php outside web doc root

zwamp_taskbartray_iconzwamp<– Z_WAMP right click menu – Right click on house icon first left in the bottom

~~~~~~~~~~~
Page with blue Z: icon is output of view script view_3cols_list.php

http://zwamp.sourceforge.net/
http://sourceforge.net/projects/zwamp/files – operating system (32-bit/i386 or 64-bit/x86_64).


MVC scripts which create this home page (ee bits 1-4 on picture above).

SPA = Single Page web Application
DOMAIN STYLE CODE structure = app. (form) scripts are in ONE subdir (and its subdirs), and index.php, if needed, is in dir above this ONE application directory.

Eg – this DEVELOPMENT MAIN MENU SCRIPTS are in zwamp dir,
– Controller script: zwamp.php and all other .php, .css, .jpg… can be in same dir. (no subdirs becouse it is small app.).

ZWAMP has no such home page, I made it from WAMPs home page, with many changes :

  1. SPA was excellent
  2. DOMAIN STYLE CODE was not implemented – everything was in appl. root dir, I had problems showing pictures (eg $imgwrench = “background: …)…
  3. MVC not clear I had work on it…

bit 1.Alati (tools), eg link instalirano

  1. view_3cols_list.php - View main body: lin.14: 
    $fleview_instalir = str_replace('zwamp.php',
    'view_instalirano_20150106.html',$flezwamp); 
    ...MENU BIT CODE lin. 36 : 
    <a style="${imgwrench}" target="_blank" href="?instalirano=1">
    instalirano/a> 
    ...CODE AT THIS PAGE BOTTOM lin 144: 
    $fleview_instalirano - Installed on my PC kod edit 
    kod_edit_run($mdpath.$rel_apldir_adr, basename($fleview_instalir));
  2. zwamp.php - Controller: if (isset($_GET['instalirano'])) {include(  
    $mdpath.$rel_apldir_adr.'/view_instalirano_20150106.html' ); exit()

Menu “2.Programi (projekti)” is list of dirs ($projectContents) not in array $projectsListIgnore

  1. zwamp_conf.php – Configuration
    1. is model script for dirs list (better not but this is simple example)
    2. and stores label “2.Programi (projekti” in array $txtlang (see script code)
    $txtlang array contains texts for more languages.

    1. Model: dirs list model code in zwamp.conf:
    $projectContents = ''; if ($idmape = opendir($mdpath)) 
    { while (false !== ($mapa = readdir($idmape)))
    { //if ($mapa != "." && $mapa != "..") 
    { if (is_dir($mdpath."/".$mapa) === true 
         && !in_array($mapa,$projectsListIgnore) ) 
    { //echo "DIRECTORY: ".$mapa.'<br />'; 
       $projectContents .= '<li>' 
      .'<a style="'.$imgdirgo.'" target="_blank" href="' 
    .( $suppress_localhost ? 'http://dev:8083/inc/fw/' : '' ) 
    .$mapa.'">'.$mapa .'</a>' .'</li>'; } else { 
    //echo "FILE: ".$mapa.'<br />'; } } closedir($idmape); }
    if (empty($projectContents)) $projectContents = 
    "<li>".$txtlang[$jezik]['txtNoProjet']."</li>\n";
  2. view_3cols_list.php – View main displays array $projectContents :
    <ul class="projects">
        ${projectContents}
    </ul>

Menu “3.Aliasi i virt.hostovi” is list of files ($aliasContents)

  1. zwamp_conf.php – Configuration (Aliases are .conf files in dir “alias” below Z:.)
    1. is model script for files list (better not but this is simple example)
    2. and stores label “3.Aliasi i virt.hostovi” in array $txtlang (see script code)
    $txtlang array contains texts for more languages.

    1. Model: files list model code in zwamp.conf:  
    $aliasContents = '';
    // ucitavanje liste (récupération) alias-a
    if ($idmape = opendir($aliasDir))
    {
    while (false !== ($konfig_file = readdir($idmape)))
    { if (!is_dir($aliasDir.$konfig_file) 
        && strstr($konfig_file, '.conf'))
    { //echo "DIRECTORY: ".$konfig_file.'<br />';
    $alias_ime = str_replace('.conf','',$konfig_file);
    $urlbit = $alias_ime;
    if ($alias_ime == 'dev') { // http://dev/
    // apache sada nece dodati localhost/ ispred dev :
    $urlbit = 'http://'.$alias_ime ;
    $alias_ime = $alias_ime . ' (vhost)';
    } else // svi ostali aliasi su kao adminer ispod .sys :
    { // /.sys/adminer tj http://localhost/adminer/
       $urlbit = 'http://dev/'.$alias_ime ;
    } ;
    $aliasContents .=
    '<li>'.'<a style="'.$imgdir.'"'
    . ' href="'.$urlbit.'">'.$alias_ime.'</a>'.'</li>';
    }
    }
    closedir($idmape);
    } else {
    //echo "FILE: ".$konfig_file.'<br />';
    }
    if (empty($aliasContents))
       $aliasContents = "<li>" . $txtlang[$jezik]['txtNoAlias']."</li>\n";
    else $aliasContents = 'Aliasi su .conf u mapi "alias" ispod Z:.'
        .'<br />'.'Služe za aplik. u mapi "apps" ispod Z:.'
        .$aliasContents;
  2. view_3cols_list.php – View main displays array $aliasContents :
    <ul class="aliases">
        ${aliasContents}
    </ul>
    

List “4.Server configuration” is list of PHP extensions ($loaded_extensions)

  1. zwamp_conf.php – Configuration
    1. is model script PHP extensions (better not but this is simple example)
    2. and stores label “3.Aliasi i virt.hostovi” in array $txtlang (see script code)
    $txtlang array contains texts for more languages.

    1. Model: PHP exstensions list model code in zwamp.conf:  
    $phpExtContents = '';
    // read liste des PHP extensions
    $loaded_extensions = get_loaded_extensions();
    // [modif oto] classement alphabétique des extensions
    setlocale(LC_ALL,"{$txtlang[$jezik]['locale']}");
    sort($loaded_extensions,SORT_LOCALE_STRING);
    foreach ($loaded_extensions as $extension)
    $phpExtContents .=
    "<li style=\"${imgplugin};\">${extension}</li>;

    view_3cols_list.php – View main displays array $phpExtContents :

    <dd><ul>${phpExtContents}</ul></dd>

 

II. Notepad Replacer – handy for taskbartrayicon-rightclickmenu, but Notepad++ workspaces, projects and sessions are enough.

http://www.binaryfortress.com/NotepadReplacer/Discussions/View/wont-install-properly-on-win2008-sp2-x64/?ID=04b032d7-ba27-49cf-b6cd-5057ec4a7b8e
Replace Microsoft Notepad with Notepad++ (windows 8) :

Regedit node created with NotepadReplacerSetup-1.1.6.exe :

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe.

string (REG_SZ) name “Debugger”
value “C:\Program Files (x86)\Notepad Replacer\NotepadReplacer.exe” /z –BEZ ” i BEZ /z

which opens J:\aplp\aplp\3_edit\3_Notepad++\notepad++.exe

III.  SCRIPTS CODE (REFACTORED Feb, 2015)

  1. C:\Windows\system32\drivers\etc\hosts

    127.0.0.1       localhost
    ::1             localhost
    127.0.0.1     dev
    ::1     dev

  2. J:\zwamp\vdrive\.sys\Apache2\conf\httpd.conf

    or   Z:\.sys\Apache2\conf\httpd.conf

  3. J:\zwamp\vdrive\.sys\Apache2\conf\vhosts.conf

    or Z:\.sys\Apache2\conf\vhosts.conf

    # ZWAMP does:   (subst Z: “J:\zwamp\vdrive”)

    # http://dev:8083/
    <VirtualHost *:8083>
    Options All
    #Options +Includes +FollowSymLinks +Indexes +MultiViews
    #+ExecCGI
    DocumentRoot “J:\dev_web\htdocs
    ServerName dev
    </VirtualHost># http://yii:8083/   – not available when listening port 8083 !?
    <VirtualHost *:8083>
    DocumentRoot “J:\dev_web\htdocs\aplyii\frontend\web
    ServerName yii
    </VirtualHost>

    # ———————————
    # http://localhost:8083/
    <VirtualHost *:8083>
    DocumentRoot “J:\zwamp\vdrive\web
    ServerName localhost
    </VirtualHost>

  4. J:\zwamp\vdrive\.sys\php\php.ini

     

    —–

  5. J:\dev_web\htdocs\index.php

    (if needed). Includes zwamp scripts – which all are outside Apache doc.root  J:\dev_web\htdocs (http://dev:8083/):require(realpath($_SERVER[‘DOCUMENT_ROOT’].’/../inc/zwamp/zwamp.php’)) ;
    exit;

  6. J:\dev_web\inc\zwamp\zwamp.php

    – Controller outside Apache doc.root  (was : J:\dev_web\htdocs\01programi\zwamp\zwamp.php)
    ….
    ************************************************************
    // 3. SHOW VIEW BITS – ee THIS SPA WEB PAGE – Single Page App.
    // ************************************************************
    echo <<< EOPG
    $pghdr
    $view_3cols_list
    <br /><br />
    $view_nppreplacer
    $view_zwamp
    $view_hosts_apachephpconf
    <br /><hr />
    $view_mongodb
    $view_f3fw
    $view_phpreports
    EOPG;/**
    * http://dev:8083/ = Apache virt.host
    *           or zwamp docroot: http://localhost/
    * J:\dev_web\inc\zwamp\zwamp.php
    *           or J:\zwamp\vdrive\web\index.php
    *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    *               ~~~ C O N T R O L L E R ~~~
    * SPA web page (Single Page App.) – no url calls
    * DOMAIN STYLE code organization – every app (page, oracle form form) own subdir
    *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    *SHOWS VIEWS: main menu (1,2,3):
    *             1.Alati (tools & help)
    *             2.Programs (projects, dirs)
    *             3.Aliases & virt.hosts
    *             4.List below (php extensions)
    *             … some help texts
    * No DBI – CRUD scripts (could be sqlite for langs).
    *        – No page params of  p o s t  type – no forms
    *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    *
    ****************************************************
    * 1. PAGE PARAMS OF G E T TYPE (ACTION COMMANDS)
    ****************************************************
    * 1.1 Display image code (from zwamp_mdl.php)
    * 1.2 Language icons in page hdr top right
    * 1.3a PAGE NAVIGATION: PATH C A L L –  I N C L U D E
    * 1.3b                  URL  C A L L – Location ()
    *
    * No page params of  p o s t  type – no forms – no DBI
    * ********************************************************
    *  2. INCLUDE VIEW BITS – LOAD THEM IN CONTENT VARIJABLES
    * ********************************************************
    *
    * ********************************************************
    *  3. SHOW VIEW BITS – ee THIS SPA WEB PAGE – Single Page App.
    * ********************************************************
    *

  7. J:\dev_web\inc\zwamp\zwamp_mdl.php

    – Configuration, set up, data (img code !)
    —–

  8. J:\dev_web\inc\zwamp\zwamp_hdr.php

    – View main hdr

  9. J:\dev_web\inc\zwamp\view_3cols_list.php

    – View main body

  10. J:\dev_web\inc\zwamp\view_zwamp.php

    – View zwamp and simmilar view code bits

  11. Helpers – utils
    J:\dev_web\htdocs\inc\utl\showsource.php

    & phpinfo & edservertxt.php &

    <?php
    // J:\dev_web\inc\utl\kod_edit_run.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
    */
    }

 

2. Install all 64bit: Oracle DB 11g XE on Windows 10 + Forms 12c + APEX 5.0.3

HOME – old downloads are here, but better https://github.com/slavkoss/fwphp/

07. August 2018

I could not find good tutorial for Forms 12 64 bit on Windows 10 64 bit, Oracle DB 11g XE.

So https://github.com/slavkoss/fwphp/tree/master/glomodul/help_sw/oracle is (I think best) tutorial for  installation (last version) Forms 11 64 bit on Windows 10 64 bit, Oracle DB 11g XE, webstart works.

20. March 2016

I successfully installed Forms 12c which seems to be much better than Forms 11g.
I installed Oracle DB 11XE 64 bit – but this article is still usefull because 64 bit 11XE is simpler installation then 32 bit.
My Newest APEX 5.0.3  installation (slowww) also works on Win10 64 bit.

Help pdf: forms12c_upgrade_forms6i_FSMFA.pdf is interresant, other help pdf-s look good. But they still are :
– language references = lists of all existing features
(sometimes also non existing – Lary Ellison’s speciality) – encyclopedias
– there are no good user manuals (tutorials) = simple recipes collections
like this I try to show here.
May be Oracle wants us to pay his expensive courses ?

All 3 ways of running “Installed successfully…” form described in
https://danielsitblog.wordpress.com/tag/oracle-forms-12c/
work for me. This is best (?) :  New standalone way for end-users to run Oracle Forms applications.

Forms 12c developer runs and connects to DB 11g XE (icon in start folder).

My .fmb & .fmx are in J:\sw\possys12 which I added to my FORMS_PATH in file
C:\oracle\midw\home_midw\user_projects\domains\base_domain\config\fmwconfig\servers\WLS_FORMS\applications\formsapp_12.2.1\config\default.env
so :
FORMS_PATH=J:\sw\possys12;C:\oracle\midw\home_midw\forms;C:\oracle\midw\home_midw\user_projects\domains\base_domain\config\fmwconfig\components\FORMS\instances\forms1

After that I could run my tipdok.fmx so :
— URL in firefox shows my tipdok.fmx screen :
http://sspc1:9001/forms/frmservlet?config=webstart&form=tipdok&userid=usr/psw@XE

https://blogs.oracle.com/proactivesupportDevTools/entry/forms_reports_12c_lifetime_support
Release Forms and Reports 12.2.x
GA Date                 Oct 2015
Premier Support Ends    Oct 2020
Extended Support Ends   Oct 2023
Sustaining Support Ends Indefinite

https://blogs.oracle.com/proactivesupportDevTools/entry/browser_less_access_to_forms
Oracle Forms 12c version can now be used without a browser while still keeping the native appearance of the application.
Either JDK or Java Plugin (JRE) has to be installed on the client PC.
An example of how to use this type of configuration can be found in the Forms web configuration file (formsweb.cfg), present in Forms 12c environment.

Download Oracle Forms12c and help :

http://www.oracle.com/technetwork/developer-tools/forms/downloads/index.html
http://docs.oracle.com/middleware/1221/formsandreports/index.html

help: https://oracle-base.com/
https://oracle-base.com/articles/12c/articles-12c
and see end this text: [L1], [L2]…

[L1] 02_fusion_admin_ASADM.pdf Administering Oracle Fusion Middleware
[L2] forms12c_deployment_FSDEP.pdf Forms Services Deployment Guide 12c (12.2.1)

 

My two attempts to install Oracle Forms 11 were unsuccessful (to big, to complicated – nobody starts new projects with Forms 11 – pity – it is great SW developed in wrong direction), but installing Oracle Forms 6i on win 8.1 64 bit and 11g (not XE) was successful (with patch 18 and that two almost 15 years old dll-s instead newer !!)

 

Forms 12c post installation

NEXT STEPS & ACTIONS WORK FOR ME :

  1. Also can be step 2. POSTINST 1  :
    Fusion Middleware Control: Weblogic Server Adminis.Console 12c
    action1111111   start Weblogic (Administration) Server:
    C:\oracle\midw\home_midw\user_projects\domains\base_domain\bin\startWebLogic.cmd
    To stop admin server, close the command shell in which it is running.action2222222  start  Weblogic Server Adminis.Console 12c :
    http://host:port/console
    http://sspc1:7001/console   user=weblogic   psw=mypsw*2
  2. Also can be step 1. POSTINST 2  Starting and Stopping Node Manageraction333333   start NodeManager utility which is used to start F or R servers :
    C:\oracle\midw\home_midw\user_projects\domains\base_domain\bin\startNodeManager.cmd
    To stop Node Manager, close the command shell in which it is running.
  3. POSTINST 3   Starting and Stopping Managed Servers (F, R…)action444444 Starting/Stopping Managed F/R Servers WLS_FORMS / WLS_REPORTS :
    http://sspc1:7001/console :
    1. Frame “Domain Configurations” -> Servers -> Control Tab
    2. chk box Managed Server eg WLS_FORMS or WLS_REPORTS
    (I have only this two plus AdminServer)
    3. button Start or Resume or ShutDown or…
  4. POSTINST 4  Running Oracle Forms
    DO NOT USE old way Embedded Java applet.action555555 run “Installed successfully…” form Standalone way from CLI (or from .bat)
    cd J:\0downl\1_instalirano\1_oracle\z_doc_oracle
    Enter the following to run your application:
    java -jar frmsal.jar -url “http://sspc1:9001/forms/frmservlet?config=standaloneapp” -t 30000action666666 run “Installed successfully…” form Java Webstart way from ibrowser
    http://sspc1:9001/forms/frmservlet?config=webutil_webstart

 

Forms 12c installation

Refer to documentation

  1.  Deinstall older java SW. Install 64bit JDK (v8U51+)
    01_jdk-8u74-windows-x64.exe 191803 kB
    I installed all as administrator
    java -version shows :
    java version “1.8.0_74”
    Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
    Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
  2. Install  W e b L o g i c  Server 12c (Infrastructure)
    fmw_12.2.1.0.0_infrastructure.jar 1498316 kB
    You do not have to create a WebLogic Server domain Forms and Reports installer can do it. In admin CLI :
    winkey+X -> Comm.prompt admin
    or winkey, cmd , right click -> run as admin
    “C:\Program Files\Java\jdk1.8.0_74\bin\java” -d64 -jar fmw_12.2.1.0.0_infrastructure.jar
    c:\oracle\midw\home_midw
    (default is C:\Oracle\Middleware\Oracle_Home)
    If you are installing Oracle Forms and Reports on MS Windows, you must make sure that the Node Manager utility that was installed with Oracle WebLogic Server is stopped before you begin Forms installation:
    2.1 Verify the Oracle WebLogic Server Node Manager utility is stopped. If it is running, kill the process.
    2.2 Determine if nodemanager.properties file is present in WebLogic_Home\common\nodemanager directory.
    C:\oracle\midw\home_midw\user_projects\domains\base_domain\nodemanager\
    a. If the nodemanager.properties file is not present, continue installing Oracle Forms and Reports.
    b. If the nodemanager.properties file does exist, open it and verify that the ListenPort parameter is included and that it is set. If the ListenPort parameter is not included or set, edit the nodemanager.properties file so that it is similar to the following, where NODE_MANAGER_LISTEN_PORT represents the port the Node Manager listens on, such as 5556:
    ListenPort=NODE_MANAGER_LISTEN_PORT
  3. Install Forms and Reports 12c (DEPLOYMENT install.type and select Oracle Home created in step 2. above that contains Infrastructure install).
    3.1 Run as admin: setup_fmw_12.2.1.0.0_fr_win64.exe  942641 kB
    3.2 you can skip Auto Update option then click to continue.
    3.3 specify installation directory (see 3.4) :
    when you are going to install forms/reports for DEPLOYMENT install. dir. is Oracle Home that contains infrastructure install:
    c:\oracle\midw\home_midw
    not eg c:\oracle\midw\home_f12c (see error 3.3 below)
    which is ok for Forms Builder STANDALONE install.
    3.4 Installation type screen: option to install Oracle Forms Builder Standalone and Oracle Forms & Reports deployment.
    Choose Oracle Forms & Reports DEPLOYMENT
    The standalone installation :
    1. does not allow forms to be run, just built and compiled. To run them, they must be deployed to a full installation of Oracle Forms and Reports Services.
    2 There is no standalone equivalent of Oracle Reports. (see https://oracle-base.com/articles/12c/standalone-forms-builder-12c-installation-on-windows-1221)
    3.5 Check system requirements screen, if OK, click next
    3.6 Installation summary screen, click next
    3.7 Installation successful screen with check box to Run Oracle Forms Configuration wizard after installation.
    Do not (?) check box and click Finish to exit the installation.In the Forms configuration wizard, you will be prompted to specify Oracle Forms Instance directory location, so just create a new directory and specify that directory for Forms instance and when the Configuration wizard will complete you can find tnsnames.Ora and other properties files in this directory.
  4. Create repository using RCU GUI:
    forms12c need to install Oracle Repository with at least following components:
    Oracle Platform Security Services containing :
    Audit Services
    Audit Services Append
    Audit Services Viewer
    C:\oracle\midw\home_midw\oracle_common\bin\rcu.bat
    host name: localhost (127.0.0.1  ?)
    service name : XE
    recommended is AL32UTF8 char.set — click “Ignore” button
    11g XE db is not certified for use in fusion midw production environm.    — click “Ignore” button
    11.2.0.4+ Ora.DB required, but XE DB is not 11.2.0.4 :
    select banner from v$version where banner like ‘Oracle Database%’;
    outputs: Oracle Database 11g Express Edition Release 11.2.0.2.0 – 64bit ProductionDEV schema prefix
    advaced replication not enabled  — click “Ignore” buttonIn SQL+ I see 9 new DEV_… users created 19.3.2016.
  5. Create WebLogic Server domain using Config Wizard GUI
    eg base_domain :
    Start GUI Configuration of Forms & Reports with :
    C:\oracle\midw\home_midw\oracle_common\common\bin\config.cmd
       chk box create new domain, located in:
    C:\oracle\midw\home_midw\user_projects\domains\base_domainapplication location:
    C:\oracle\midw\home_midw\user_projects\applications\base_domainadmin user name = weblogic
    psw = your choice (8 char, min. one num or…)autoconfig – RCU data (not manual)vendor OracleDBMS/service  XE (ora7 ?)
    schema owner  DEV_STB
    host name localhost (dbhost.example.com)  port 1521created domain : base_domain
    C:\oracle\midw\home_midw\user_projects\domains\base_domainWebLogic Admin server URL:
    http://sspc1:7001/console
  6. Start all servers (how: see postinst above) :
    Node Manager=util which starts not Admin servers, Domain servers are: Admin Server, forms server and reports server and other servers if you have them (I do not)

 

 

 

May 2014

1. http://www.snapdba.com/2013/04/installing-apex-4-2-and-configuring-embedded-plsql-gateway-epg/
2. https://community.oracle.com/thread/2344127?tstart=0
3. http://dbswh.webhop.net/apex/f?p=BLOG:HOME:0

1a. http://www.oracle.com/technetwork/developer-tools/apex/application-express/upgrade-apex-for-xe-154969.html  — upgrade was unsuccessfull

Here:
– drop all APEX_ and FLOWS_ schemas,
– and fresh install APEX 4.2.5 which was unsuccessfull
(same as upgrade APEX 4.0 -> 4.2.5)

Thank’s to link 1.(snapdba, especcialy Nancy Schorr) I solved partially this, except  can not load images becouse of invalid VIEW XDB.PATH_VIEW
– and this is my question to more experienced people:

conn xdb/xdb@XE
start C:\oraclexe\app\oracle\product\11.2.0\server\bin\sredi (standard code to show invalid DB objects) outputs:
— 3 invalids can not be compiled:
ALTER TRIGGER XDB.XDB_PV_TRIG COMPILE;
ALTER PACKAGE BODY XDB.DBMS_XSLPROCESSOR COMPILE;
ALTER VIEW XDB.PATH_VIEW COMPILE;

Goal details
==========

At this job end I have:
1. working link http://127.0.0.1:8080/apex/apex_admin
(no error: “Unable to run page sentry in application 4500”)
2. select * from all_users order by username;
ee:
start C:\oraclexe\app\oracle\product\11.2.0\server\bin\users.sql

USERNAME      USER_ID  CREATED
---------------------  ----------
ANONYMOUS        35    27-AUG-11
APEX_040200      59    24-MAY-14
APEX_PUBLIC_USER 58    24-MAY-14
CTXSYS           32    27-AUG-11
FLOWS_FILES      57    24-MAY-14
HR               43    27-AUG-11
...

What / how  do to reach goal
========================
A standard Oracle 11.2.0.3 32 bit database installation comes bundled with
APEX (Application Express) 3.2.1 by default (DB 11XE R2 32 bit – with 4.0).

  1. upgrade DB 11XE R2 to latest version of APEX (currently 4.2.5 – 24.5.2014)
  2. configure EPG (Embedded PL/SQL Gateway):
    – which uses Oracle XML DB HTTP components within DB itself
    – so no need to run a separate HTTP server.
  1. Download apex_4.2.5.zip from :
    http://www.oracle.com/technetwork/developer-tools/apex/downloads/index.html
  2. Drag apex folder from apex_4.2.5.zip, to drop to C:
  3. C:\apex> sqlplus /nolog
    SQL> CONNECT SYS/YOURSYSPSW@XE as SYSDBA
  4. drop user APEX_040000 cascade;
    drop user APEX_040200 cascade;  — upgrade created this – was unsuccessfull
    drop user APEX_PUBLIC_USER cascade;
    drop user FLOWS_FILES cascade;
  5. @apexins APEX APEX TEMP /i/
    Usage: @apexins <apex_tbs> <apex_files_tbs> <temp_tbs> <images>
    apex_tbs – name of the tablespace for the APEX user.
    apex_files_tbs – name of the tablespace for APEX files user.
    temp_tbs – name of the temporary tablespace.
    images – virtual directory for APEX images. Define the virtual
    image directory as /i/ for future updates.
  6. Log back into SQL*Plus (as above)
    C:\apex> sqlplus /nolog
    CONNECT SYS/YOURSYSPSW@XE as SYSDBA
    change ADMIN account password:
    @apxchpwd
    NOTE: password must min length=6, and contain at least :
    one uppercase letter, one lowercase, one number
    one punctuation character: ( !”#$%&()“*+,-/:;?_ )
  7. ALTER USER anonymous ACCOUNT UNLOCK;
    alter user ANONYMOUS identified by NULL;
    Changed ANONYMOUS password to NULL.
    ALTER USER xdb ACCOUNT UNLOCK;
    ALTER USER apex_public_user ACCOUNT UNLOCK;
    ALTER USER flows_files ACCOUNT UNLOCK;
  8. select username, account_status from dba_users where username = ‘XDB’;
    Database     User Status
    —————   ——-
    XDB             EXPIRED
    if EXPIRED & LOCKED then
    first : alter user xdb account unlock;     then:
    alter user XDB identified by XDB;
  9. Script to enable anonymous access to XML DB repository.
    Some people said that it didnt help (helped me). Run it if:
    start C:\oraclexe\app\oracle\product\11.2.0\server\rdbms\admin\epgstat.sql
    outputs: Allow repository anonymous access?   FALSE
            shows also:    nls-language             american_america.al32utf8
    SET SERVEROUTPUT ON
    DECLARE
    l_configxml XMLTYPE;
    l_value VARCHAR2 (5) := ‘true’; — (true/false)
    BEGIN
    l_configxml := DBMS_XDB.cfg_get ();IF l_configxml.
    EXISTSNODE (
    ‘/xdbconfig/sysconfig/protocolconfig/httpconfig/allow-repository-anonymous-access’) =
    0
    THEN
    — Add missing element.
    SELECT INSERTCHILDXML (
    l_configxml,
    ‘/xdbconfig/sysconfig/protocolconfig/httpconfig’,
    ‘allow-repository-anonymous-access’,
    XMLType (
    ‘<allow-repository-anonymous-access xmlns=”http://xmlns.oracle.com/xdb/xdbconfig.xsd”>’
    || l_value
    || ‘</allow-repository-anonymous-access>’),
    ‘xmlns=”http://xmlns.oracle.com/xdb/xdbconfig.xsd”‘)
    INTO l_configxml
    FROM DUAL;DBMS_OUTPUT.put_line (‘Element inserted.’);
    ELSE
    — Update existing element.
    SELECT UPDATEXML (
    DBMS_XDB.cfg_get (),
    ‘/xdbconfig/sysconfig/protocolconfig/httpconfig/allow-repository-anonymous-access/text()’,
    l_value,
    ‘xmlns=”http://xmlns.oracle.com/xdb/xdbconfig.xsd”‘)
    INTO l_configxml
    FROM DUAL;DBMS_OUTPUT.put_line (‘Element updated.’);
    END IF;DBMS_XDB.cfg_update (l_configxml);
    DBMS_XDB.cfg_refresh;
    END;
    /outputs: Element inserted.
    start C:\oraclexe\app\oracle\product\11.2.0\server\rdbms\admin\epgstat.sql
    — Now is: Allow repository anonymous access? TRUE !!!
  10. and load images:
    SQL> @apxldimg.sql APEX_HOME
    -> SAME ERR UPGRADE AS AFTER DROP APEX_ & FLOW_ USERS:
    ======================================================
    PL/SQL: ORA-04045: errors during recompilation/revalidation of XDB.PATH_VIEW
  11. Configure database parameters for APEX
    Check that the JOB_QUEUE_PROCESSES parameter is set to at least 20:
    SHOW PARAMETER job_queue_processes
    ALTER system SET job_queue_processes=20 scope=both;For a small group of concurrent users, Oracle recommends a value
    of 5 for SHARED_SERVERS:
    SHOW PARAMETER shared_servers
    ALTER system SET shared_servers=5 scope=both;
  12. Enable network services (ACL) and XML DB HTTP server
    Re enable the Oracle XML DB HTTP Server port (8080):
    EXEC dbms_xdb.sethttpport(8080);Enable remote HTTP connections (optional):
    EXEC dbms_xdb.setListenerLocalAccess(l_access => FALSE);
    If l_access is set to TRUE, setListenerLocalAccess allows access to
    XML DB HTTP server on the localhost only.
    If l_access is set to FALSE, setListenerLocalAccess allows access to
    XML DB HTTP server on both the localhost and non-localhost interfaces
    i.e. remote connections.By default, the ability to interact with network services is disabled
    in Oracle Database 11g.
    Therefore, you must use DBMS_NETWORK_ACL_ADMIN package to grant
    connect privileges to any host for the APEX_040200 database user:DECLARE
    ACL_PATH VARCHAR2(4000);
    BEGIN
    — Look for the ACL currently assigned to ‘*’ and give APEX_040200
    — the “connect” privilege if APEX_040200
    — does not have the privilege yet.
    SELECT ACL INTO ACL_PATH FROM DBA_NETWORK_ACLS
    WHERE HOST = ‘*’ AND LOWER_PORT IS NULL AND UPPER_PORT IS NULL;
    IF DBMS_NETWORK_ACL_ADMIN.CHECK_PRIVILEGE(
    ACL_PATH, ‘APEX_040200’, ‘connect’
    ) IS NULL THEN
    DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(
    ACL_PATH, ‘APEX_040200’, TRUE, ‘connect’
    );
    END IF;
    EXCEPTION
    — When no ACL has been assigned to ‘*’.
    WHEN NO_DATA_FOUND THEN
    DBMS_NETWORK_ACL_ADMIN.CREATE_ACL(‘power_users.xml’,
    ‘ACL that lets power users to connect to everywhere’,
    ‘APEX_040200’, TRUE, ‘connect’);
    DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(‘power_users.xml’,’*’);
    END;
    /
    COMMIT;ERROR at line 1: ~~~~~~~~~~~~ !”#$%&()“*+,-/:;?_ ~~~~~~~~~~~~
    ORA-04045: errors during recompilation/revalidation of XDB.PATH_VIEW
    ORA-00600: internal error code, arguments: [qctchr : bfl], [4000], [1024], [1],
    [170], [2], [175], [], [], [], [], []
    ORA-06508: PL/SQL: could not find program unit being called:
    “SYS.DBMS_NETWORK_ACL_ADMIN”
    ORA-06512: at line 19   ORA-01403: no data found

 

OTHER USEFULL (?) COMMANDS

COL comp_name FOR A30
SELECT comp_name, version, status FROM dba_registry WHERE comp_id=’APEX’;

COMP_NAME                         VERSION      STATUS
Oracle Application Express     4.2.5.00.08    VALID

– It’s hard to find error messages with Apex. To get error messages:
execute dbms_epg.set_global_attribute(‘log-level’, 7);
Then look for logs in your Oracle trace directory that start with _s .
Or grep for epg or apex in that directory and you’ll see some errors.

– Switching to brand new port. I made up 8083 as I wasn’t sure
some settings somewhere were still stuck for 8080.
Setting it back and forth to 0 and back to 8083 seems to help.
EXEC dbms_xdb.sethttpport(8083);
EXEC dbms_xdb.sethttpport(0);
EXEC dbms_xdb.sethttpport(8083);

– 8083 never showed up in netstat but it doesn’t seem to make a difference

Test scripts: ed C:\oraclexe\app\oracle\product\11.2.0\server\bin\t.sql
start C:\oraclexe\app\oracle\product\11.2.0\server\bin\t.sql
ed C:\apex\tmp.txt – output of t.sql

1. Install Apache, PHP, Oracle DB 11g XE & 11g, Oracle Forms 6i and 12c & Reports on Win 10 (all 64bit)

HOME   Downloads are now:  https://github.com/slavkoss/fwphp

26.oct.2019 I use 64 bit XAMPP: PHP 7.3.7 AND APACHE 2.4.38. on Windows 10 64 bit. PDO: mysql, oci, sqlite ARE WORKING.Oracle db 11gXE (no more E. Rangel pdooci – pdo sintax on oci8 program layer).

I tried 4-5 WAMP server SW WAMP and ZWAMP are ok but XAMPP unzip is  simplest – almost 1 click . WAMP has problem with composer installations from Windows command line. Next shows more details (I do not use ZWAMP recently) :

  1. Unzip zwamp-x64-2.2.1-full.zip to J:\zwamp64
  2. Rename old J:\zwamp64\vdrive\.sys\Apache2 dir to Apache2_2_4_16
  3. Download Apache : http://www.apachelounge.com
    Create dir J:\zwamp64\vdrive\.sys\Apache2.
    Unzip httpd-2.4.20-win64-VC14.zip – it’s Apache24 folder content to :
    ServerRoot J:/zwamp64/vdrive/.sys/Apache2 (or c:/Apache24 = ServerRoot) in httpd.conf
    Default folder for your your webpages is DocumentRoot
    DocumentRoot J:/zwamp64/vdrive/.sys/Apache2/htdocs
    Directories
    ScriptAlias – also when you use extra folder config file(s) change to your location there
  4. No changes in J:\zwamp64\vdrive\.sys\Apache2_2_4_16\conf\extra
  5. changes in J:\zwamp64\vdrive\.sys\Apache2\conf :
    copy here own httpd.conf ver 2.4.16 to conf dir (rename original before copying)
    copy here own vhosts.conf ver 2.4.16 to conf dir
  6. J:\zwamp64\vdrive\.sys\Apache2\conf\httpd.conf :
    LoadModule php7_module /.sys/php/php7apache2_4.dll
    AddType application/x-httpd-php .php# without this OCI8 and PDO MySQL and PDO sqlite are not visible in inet browser
    are visible in php CLI
    PHPIniDir /.sys/php
  7. Download PHP:  http://windows.php.net/download/
    php-7.0.8-Win32-VC14-x64.zip
     same unzip as apache zip above.
  8. Downloadnewest oci8-2.1.1 Extension for php 7.0.8 64 bit on Windows 10 64 bit – april 2016 (older does not work) :
    OCI8 is also for E.Rangels PDOOCI.
    https://pecl.php.net/package/oci8/2.1.1/windows  – Windows dll-s
    https://pecl.php.net/package/oci8   – for Linux
    released by [email protected]
    https://blogs.oracle.com/opal/entry/php_7_oci8_2_1
    php_oci8-2.1.1-7.0-ts-vc14-x64_Jones_pecl.php.net.zip
    Unzip it’s dll-s to: 
    J:\zwamp64\vdrive\.sys\php\ext
    Christopher Jones’s oci8-2.1.1 for php 7 :
    dir J:\zwamp64\vdrive\.sys\php\ext\php_oci*.*
    18.04.2016.  05:20   155.136 php_oci8.dll
    18.04.2016.  05:20   790.528 php_oci8.pdb – WHAT IS THIS ?
    18.04.2016.  05:20   157.184 php_oci8_11g.dll
    18.04.2016.  05:20   790.528 php_oci8_11g.pdb
    18.04.2016.  05:20   158.208 php_oci8_12c.dll
    18.04.2016.  05:20   790.528 php_oci8_12c.pdb
    7 File(s)      2.997.248 bytes
  9. J:\zwamp64\vdrive\.sys\php\php.ini
  10. 2click J:\zwamp64\zwamp.exe -> right click house icon -> restart
    -> started Apache and MySQL. If not both started very useful is :
    J:\zwamp64\vdrive\.sys\Apache2\bin>httpd.exe
    – this report errors, same as :
    php -v
    PHP 7.0.8 (cli) (built: Jun 21 2016 15:15:15) ( ZTS )
    Copyright (c) 1997-2016 The PHP Group
    Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    or
    php -r “var_dump(function_exists(‘oci_connect’));”
    or
    C:\WINDOWS\system32>php -r “if (! $dbc = oci_pconnect(‘hr’, ‘hr’, ‘ora7’, ‘UTF8’)) echo ‘***** 1. UNSUCCESSFULL db connect *****’; else {echo \”\n\”; echo ‘~~~~~~~~~~~  PARSE-BIND-EXECUTE-FETCH ~~~~~~~~~~’; echo \”\n\”; echo ‘SUCCESSFULL db connect’; echo \”\n\”;  $dml=’SELECT first_name, phone_number FROM (SELECT first_name, phone_number FROM employees ORDER BY first_name) where rownum < 3′; echo $dml; echo \”\n\”; $parse_stid = oci_parse($dbc, $dml);  if (!$parse_stid) {   $m = oci_error($conn);    echo ‘skripta: ‘ . __FILE__ ;   echo’ says neuspio oci_parse : ‘ ;   echo $m[‘message’]; }   $exec_ret =  oci_execute($parse_stid);       echo ‘oci_execute returned: ‘;     print_r($exec_ret);        echo \”\n\”; echo ‘oci_fetch_array returned: ‘;  $row = oci_fetch_array($parse_stid, OCI_ASSOC + OCI_RETURN_NULLS); print_r($row); }”WHICH DISPLAYS:~~~~~~~~~~~  PARSE-BIND-EXECUTE-FETCH ~~~~~~~~~~
    SUCCESSFULL db connect
    SELECT first_name, phone_number FROM (SELECT first_name, phone_number FROM employees ORDER BY first_name) where rownum < 3
    oci_execute returned: 1
    oci_fetch_array returned: Array
    (
    [FIRST_NAME] => Adam
    [PHONE_NUMBER] => 650.123.2234
    )
  11. Not needed if we use ZWAMP :
    Install Apache as a service:
    httpd.exe -k install   httpd.exe -k restart, or stop
    ApacheMonitor: (not needed if we use ZWAMP):
    Double click ApacheMonitor.exe, or put it in your Startup folder.

 

 

28.11.2015 INSTALLATION APACHE 2.4.16 (Win64)  & PHP 7.0.0 RC5 MSVC14 (Visual C++ 2015) x64 ON J:\zwamp\vdrive\.sys\Apache2 (& …\php) OR ON C DISK

Install as a service (not needed if using XAMPP or ZWAMP or WAMP):
httpd.exe -k install
httpd.exe -k  restart

httpd.exe -k stop  
ApacheMonitor: Double click ApacheMonitor.exe, or put it in your Startup folder.

Use PGP Signature and/or the SHA Checksums to verify the integrity.
C:\gnuwin32\bin\sha1sum.exe -help
sha1sum.exe -b J:\0downl\1_instalirano\Apache_httpd-2.4.16-win64-VC14.zip :
d917094cf13ecea83938aa58058ea7c5c6ef2756
Checksums created with GPGHash by SmartJuwel
Creation date: 22.07.2015 with GnuPG Version: 1.4.18
SHA1-Checksum for: httpd-2.4.16-win64-VC14.zip:
D917094CF13ECEA83938AA58058EA7C5C6EF2756
or SHA224-Checksum or SHA256-Checksum or SHA384-Checksum

php http://windows.php.net/qa/ For Apache on Windows: Thread Safe

I did not used Instant Client :
Oracle Database drivers for popular languages and environments including Node.js, Python and PHP can use Instant Client to connect to local or remote DBs.
https://community.oracle.com/thread/1051752 :
1. Download 64 or 32-bit Oracle Instant Client – there is no MSVC14 build 6.8.2015
2. put it somewhere useful (I put it under my php directory)
3. Add it to your system’s PATH. (A quick & dirty fix on Windows is to copy
all of the *.dll files   from the 11g InstantClient package* into same dir
as your webserver’s executable (e.g. httpd.exe).)
http://www.oracle.com/technetwork/database/features/instant-client/index-100365.html
4. Edit your php.ini and uncomment extension=php_oci8_11g.dll

I did not 1. to 4. above !

Stories on WEB that excellent (but never finished and abandoned) old 32 bit F6i, R6i SW and new Windows do not work together are not true. See also – they managed same as I (good site, could give more details):
Installing Oracle Developer (forms & reports 6i) on Windows 7 64bit
and
Oracle Developer 6i & Oracle database 11g R2 (11.2.0.1.0)
Great question to Oracle who says “we do not leave our customers who still use F6i & R6i” – but F6i & R6i can not even be downloaded from Oracle:
Why 32 bit F6i & R6i can not be patched to be certificated on 64 bit 11g and 64 bit Windows ? Oracle Power Objects (SW simmilar to F6i & R6i ) is also abandoned SW.
Today is modern to make new SW, worse than old (nobody begins new project with F11 & R11 – they are rather applications than development SW) and forget poor bastards who bought abandoned SW. So they cut their expenses (and our lifes). Simmilar stories are Microsoft ASP-VS2008-Silverlight, Google AngularJS 1.x – 2.x, over 100 development SW around…
Incompetent managers and trade departments cut our lifes, but they should know that life is not child joke.

APACHE – USE THREAD SAFE (TS) VERSIONS OF PHP BINARIES FOR WINDOWS
MULTITHREAD CAPABLE BINARIES BUILDS – INTERACTION WITH A MULTITHREADED SAPI AND PHP LOADED AS A MODULE INTO A WEB SERVER.

Use Apache builds provided by Apache Lounge – PHP official site use their binaries to build Apache SAPIs.

VC9 builds require you to have the Visual C++ Redistributable for Visual Studio 2008 SP1 x86 or x64 installed.
VC11 builds require to have the Visual C++ Redistributable for Visual Studio 2012  x86 or x64 installed. and so on

PECL FOR WINDOWS:PECL extensions for Windows is being worked on. Windows DLL can be downloaded right from thePECL website.
PECL extension release and snapshot build directories are browsable directly.

Not needed for 64 bit Apache, PHP, 11XE all three on home PC..
Download from http://www.oracle.com/technetwork/topics/winx64soft-089540.html
instantclient-basic-windows.x64-12.1.0.1.0.zip (64,939,911 bytes) (cksum – 3658834848)
extract to:  C:\Windows\SysWOW64\instantclient_12_1
and set it on Win PATH variable + H:\Apache24\bin,
(NO:    THERE ARE MORE PHP INSTALLATIONS  (only 1 Apache):    + H:\php)

C:\Apache24\bin>echo %SystemRoot%
outputs: C:\Windows

C:\Apache24\bin>path

extension=php_oci8_11g.dll (remove the “;” from the start of the line)

Restart Apache (XAMPP or WAMP server)

Open  file to see database name as
C:\oraclexe\app\oracle\product\11.2.0\server\network\ADMIN\tnsnames.ora
// D:\app\Farhan\product\11.2.0\dbhome_1\NETWORK\ADMIN\tnsnames.ora

LISTENER_ORCL =
(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))

ORACLR_CONNECTION_DATA =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
(CONNECT_DATA =
(SID = CLRExtProc)
(PRESENTATION = RO)
)
)

ORCL =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl.168.1.2)
)
)

Service name is “orcl.168.1.2”,  my is: XE :

XE =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = sspc)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  ) 
ora7 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = sspc)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  ) 
EXTPROC_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1))
    )
    (CONNECT_DATA =
      (SID = PLSExtProc)
      (PRESENTATION = RO)
    )
  )

ORACLR_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1))
    )
    (CONNECT_DATA =
      (SID = CLRExtProc)
      (PRESENTATION = RO)
    )
  )

See  https://github.com/slavkoss/fwphp/blob/master/fwphp/glomodul/z_examples/index.php
for  httpd.conf, vhosts.conf, C:\Windows\System32\drivers\etc\hosts, php.ini