12. PHP menu & CRUD code skeleton (B12phpfw) comparison


Revised November 2021 October 2020, December 2019. Original post was published in February 2019.
Download https://github.com/slavkoss/fwphp/ . My articles before this are not needed to learn B12phpfw.

November 2021 version 8.0.0 (requires PHP ver. >= 7). Download  contained many learning examples, but no more because three modules – packages :  Mnu (menu code skeleton), Msg (Blog – compound module) and Mkd  contain enough (not to simple)  PHP code for learning, create large sites CRUD pages and own WYSIWYG web editor. To simple learning code (frequent in learning sources books and internet) is dangerous, leads to wrong coding, which is one of reasons why I have 8 versions. Version 8 is in my opinion smallest code to learn serious web programing.

What’s new in version 8 :

  1. Autoload.php is refactored, much simpler, see old z_NOT_USED_TO_COMPLICATED_Autoload_1stVersion.php
    This is good case showing that first code versions are to complicated code – very frequent case – which is one of reasons why I have 8 versions.
  2. Shares are not any more in \zinc folder but in in vendor folder eg \vendor\b12phpfw\Autoload.php (below web server doc root eg J:\awww\www\). Site roots are on same place in folders hierarchy eg \fwphp (may be named site_x). Also group of modules eg \fwphp\glomodul. Also modules eg \fwphp\glomodul\blog – dir like oracle Forms form module.
  3. PHP 8 (ver. 7 still works) and Bootsrap 5
// J:\awww\www\fwphp\www\index.php
namespace B12phpfw\site_home\www ;

use B12phpfw\core\b12phpfw\Autoload ;

//1. settings - properties - assign global variables to use them in any code part
$module_path = str_replace('\\','/', __DIR__) .'/' ;
$site_path = dirname($module_path) .'/' ; //to app dir eg "glomodul" dir and app
//to web server doc root or our doc root by ISP $module_towsroot = eg '../../../'
$wsroot_path = dirname(dirname($module_path)) .'/' ;
//or $wsroot_path = str_replace('\\','/', realpath('../../')) .'/' ;
$shares_path = $wsroot_path.'vendor/b12phpfw/' ; //includes, globals, commons, reusables

$pp1 = (object)
[ 'dbg'=>'1', 'stack_trace'=>[str_replace('\\','/', __FILE__ ).', lin='.__LINE__]
, 'module_version'=>' Mnu' //, 'vendor_namesp_prefix'=>'B12phpfw'
// $_SESSION["TrackingURL"]=$_SERVER["PHP_SELF"];
// 1p. (Upper) Dirs of clsScriptsToAutoload. With 2p(ath). makes clsScriptToAutoloadPath
// 2p. Dir name of clsScriptToAutoload is last in namespace and use (not full path !).
, 'wsroot_path' => $wsroot_path // to awww/www (or any names)
, 'shares_path' => $shares_path // to b12phpfw, b12phpfw is required dir name
, 'site_path' => $site_path // to fwphp (or any names)
, 'module_path' => $module_path // to fwphp/www (or any names)
, 'glomodul_path' => $site_path .'glomodul/'
, 'examples_path' => $site_path .'glomodul/z_examples/'
] ;

//2. global cls loads classes scripts automatically
require($pp1->shares_path . 'Autoload.php');
new Autoload($pp1);

//3. process request from ibrowser & send response to ibrowser :
//Home_ ctr "inherits" index.php ee inherits $p p 1
$module = new Home_ctr($pp1) ; //also instatiates higher cls : Config_ allsites

// J:\awww\www\vendor\b12phpfw\Autoload.php
namespace B12phpfw\core\b12phpfw ; //Dir name is last in namespace and use 
//use B12phpfw\dbadapter\post\Tbl_crud ; // as Tbl_crud_post ;

class Autoload
protected $pp1 ; //M O D U L E PROPERTIES PALLETE like in Oracle Forms

public function __construct(object &$pp1) {
$pp1->stack_trace[]=str_replace('\\','/', __FILE__ ).', lin='.__LINE__ .' ('. __METHOD__ .')';
$this->pp1 = $pp1 ;
spl_autoload_register(array($this, 'autoloader'));
return null ;

function autoloader(string $nscls) 
//$nscls is namespaced called cls name eg B12phpfw\module\book\Home_ctr
$nscls_linfmt = str_replace('\\',$DS, $nscls) ; //ON LINUX
$clsname = basename($nscls_linfmt) ; //eg Home_ctr, Config_ allsites, Db_allsites
$module_dir = basename(dirname($nscls_linfmt)) ; //eg Home_ctr, Config_ allsites, Db_allsites
//echo '<pre>$nscls='; print($nscls) ; echo ' $module_dir='; print($module_dir) ; echo '</pre>';

switch ($module_dir) {
case 'b12phpfw': 
$clsscript_path=dirname($this->pp1->shares_path).'/'.$module_dir.'/'.$clsname .'.php' ; 
$clsscript_path=dirname($this->pp1->module_path).'/'.$module_dir.'/'.$clsname .'.php' ; break;
//echo '<p>$clsscript_path='; print($clsscript_path) ; echo ' SAYS: '. __METHOD__ .'</p>';

require $clsscript_path;


2020.09.30 DONE version 7.0.0 (declare(strict_types=1);).

DBI is improved with trait Db_allsites instead class Db_allsites. Each DB (persistent storage) adapter-class named Tbl_crud :

  1. use B12phpfw\core\zinc\Db_allsites
  2. implements Interf_Tbl_crud

This means that :

  1. eg blog module in blog folder, works much easier with more model classes ee Tbl_crud -s, ee with own Tbl_crud and with other tables Tbl_crud.
  2. eg sole module controller class Home_ctr extends class Config_allsites, no more extends also two DB CRUD classes which is unnatural (but seems easy because logically : all is in Home_ctr).

B12phpfw core (CRUD) code – How to get ONLY banana ?

It is not easy to see need to why convert code from procedural MVC to OOP MVC with namespaces and autoloading. For navigation (url-s, links) code procedural and OOP is same – OOP does not help. Procedural MVC user module code is more clear and readable. So why is OOP better ?

Some say: “is OOP mistake ?” – eg lack of reusability in OOP – to get banana (some method or attribute) you get also gorilla holding banana and whole gorilla`s jungle (all higher classes with complicated dependencies). It is why B12phpfw code skeleton is for CRUD modules (is not required for Mnu and Mkd modules but I put both on B12phpfw code skeleton).

Interfaces help to get ONLY banana, but coding is complicated – I could find only strong-talk-weak-work code examples about advanced use of interfaces.

  1. My not complicated interface Interf_Tbl_crud I made to standardize coding Tbl_crud model adapter classes and Db_allsites shared model class. Each simple (ee one table) module like “invoice items” module has own dir and own Tbl_crud class leading to more than 100 Tbl_crud.php model adapter classes scripts in big application (eg material and financial book keeping).
    J:\awww\www\vendor\b12phpfw\Interf_Tbl_crud.php (... hits)
    Line 20: static public function get_cursor( //instead rr
    Line 28: static public function cc(object $pp1, array $other=[]): object ; //create row
    Line 31: static public function rrnext(object $cursor ): object ; //read next from $c=get_cursor
    Line 34: static public function rrcnt( //string $sellst, //read count rows in table
  2. With DBI trait Db_allsites I eliminated two higher DB classes. So if banana (some method or attribute) is get_cursor (read) from more tables it is not in two higher DB classes which seems simplest solution but caused complicated coding in version 6. Eg invoice module works with two (or three – bill) tables : invoice and invoice_items.get_cursor banana is not in jungle (two higher DB classes) any more, gorilla and jungle is only one abstract class Config_allsites which is de facto assigning $pp1 = properties (Oracle Forms form property palete made global).
    1. Banana $pp1 = properties palette may cause difficulties in aggregate (compound, composed, multiplex) modules like Blog, Invoice… but $pp1 is inevitably (imminence, necessity) gorilla-jungle and can not be further simplified. I worked 20 years with $pp1 and globals jungle (Oracle Forms 6i) which i not so well accomplished as in B12phpfw .

    See https://phpthewrongway.com/, or Joe Armstrong why OOP sucks http://harmful.cat-v.org/software/OO_programming/why_oo_sucks.

    Similar “simplest solution” three dirs M,V,C is (I think) bad, I have opinion -:) for 3dirs lovers who put in foreground coding technic (M,V,C code separation) instead pages (functionality, business logic , eg invoice page).

During winter 2019/2020 year (much to late because I tested lot what others did) I made Version 6. of menu and CRUD PHP code skeleton (own framework named “B12phpfw”) – core code is about 50 kB.
I also made posts module “Msg” with posts edited with WYSIWYG Markdown SimpleMDE editor (or HTML WYSIWYG Sumernote editor or … any editor you wish). Msg module is based on B12phpfw, also very small code.

B12phpfw code skeleton and Msg application on B12phpfw skeleton is minimal PHP code to learn (medium/advanced knowlege) PHP !

Why ?

  1. I do not like proposed solutions in best php frameworks (Laravel, Simfony, Yii…) and learning sources (internet, books).
  2. I think that eg module invoice php code should be in own folder like Oracle Forms form invoice.fmb instead all forms/reports in 3 folders: M, V, C).
  3. I think that should be simple/fast/professional : shares (globals), routing, dispaching, classes loading , web rich text editing – it is why I spend many hours coding my B12phpfw (huge time wasting which should do software authors, not sw users-programers like me).

Compared B12phpfw Msg (blog) module and TraversyMVC blog module and Mini3 module URLs Youtube songs adresses

Why I do not like proposed solutions in existing PHP frameworks and what I did to (I hope) improve fw code. Red colored features and three asterisks *** are main reasons for B12phpfw, but I improved also other features.

TraversyMVC (has video) and Mini3 are simplified, with some (many?) differences Laravel, Simfony, Yii… B12phpfw is much more different – see red colored features.

Feature B12phpfw TraversyMVC and Mini3
1. ***Modules in own folder like Oracle Forms .fmb has – it is main reason for B12phpfw ! has not like Mini3 – all forms/reports in 3 folders: M, V, C
2. Name spaced classes (functional name spacing) has has not – no functional, no positional (paths) name spacing, Mini3 MVC PHP fw which is in my opinion better than TraversyMVC : https://github.com/panique/mini3 has name spaced classes
3. Number of folders (my opinion) optimal to many like Mini3
4. Minimal PHP code to learn (medium) PHP (my opinion) optimal (but we could add additional code) good but not enough like Mini3 eg see WYSIWYG, globals…
5. Functional methods, attr. etc naming (my opinion) good could be better
6. ***Global classes, methods etc (my opinion) good bad like Mini3
7. (Posts edited with any) WYSIWYG editor has has not like Mini3
8. ***Home_ctr or Home_mdl CRUD layer methods do not know for underlaying Db_allsites layer PDO methods, MySql, Oracle… has much improved has not like Mini3
9. OOP has has like Mini3
10. namespaces (own PSR-4 (or Composer’s) autoloading classes scripts) improved has not, Mini3 has
11. ***All scripts are included (ee no http jumps except some jumps to other module) has has not, Mini3 has, B12phpfw took it from Mini3
12. jQuery only for Bootstrap 5 yes yes, Mini3 has own CSS
13. no AJAX, no JSON yes yes, Mini3 has basic jQuery AJAX explained
14. server side validation has has like Mini3
15. authentification (log in / out) has has, Mini3 has not
16. authorization (only logged in users may execute some code ee CRUD code…) has has, Mini3 has not
17. ***Own debugging very simple and useful : msg in pre tag or popup JS msg). xdebug also helps. has has not like Mini3

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;
 * 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
 $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
 * 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');
 // ******************************************

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)

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.

-- winkey+X -> Comm.prompt admin
cd C:\oraclexe\app\oracle\product\11.2.0\server\bin
sqlplus hr/hr
sho user
  ID          NUMBER(10) NOT NULL,
  SIGN        VARCHAR2(11),
  SYMBOL      VARCHAR2(13),
  PLANET      VARCHAR2(7),

  -- PRIOR TO 11G :
  -- 11G :
sho err

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 :


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


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~~~