- Over many years in the industry, I have developed what people call "funky programming habits".
- Here is a quick sharing of some "funky coding", hopefully tickle the brains of some beginners.
(PART 1) CORE CLASS
YAWN
// SOME.PHP
namespace SOMETHING;
class MYLIB { ... }
// ANOTHER.PHP
require "SOME.PHP";
$OBJECT = new SOMETHING\MYLIB();
LIB-CORE.PHP
// COREBOXX CLASS
class CoreBoxx {
public $modules = [];
function load ($module) : void {
require "LIB-$module.php";
$this->modules[$module] = new $module($this);
}
}
// COREBOXX OBJECT & LOAD MODULE
$_CORE = new CoreBoxx();
$_CORE->load("DB");
- For those who are lost,
$_CORE->load("DB")
simply:require "LIB-DB.php";
$_CORE->modules["DB"] = new DB($_CORE);
- "Object in array" seem stupid at first, but this enforces a "standard library format".
LIB-Module.php
Class Module extends Core { ... }
- More tricks below.
(PART 2) MAGIC POINTER LINKS
MAGIC GET
class CoreBoxx {
function __get ($name) {
if (isset($this->modules[$name])) { return $this->modules[$name]; }
}
}
- "By default", we access the Database module with
$_CORE->modules["DB"]
(or$this->modules["DB"]
in functions). - We use
__get()
to "shorten" it to$_CORE->DB
or$this->DB
.
MAGIC LINK
// LIB-CORE.PHP
class Core {
public $Core;
function __construct ($core) { $this->Core =& $core; }
function __get ($name) {
if (isset($this->Core->modules[$name])) { return $this->Core->modules[$name]; }
}
}
// LIB-DB.PHP
class DB extends Core {}
- Take note,
$_CORE->modules["DB"] = new DB($_CORE)
. Yes, we pass$_CORE
intonew DB()
. - What happens here:
$_CORE->DB->Core =& $_CORE
- Same magic
__get()
trick. In all functions ofDB
,$this->Core
will refer back to$_CORE
.
SIBLING LINK
// LET'S SAY, WE ADD A DUMMY FUNCTION TO THE DATABASE MODULE
class DB extends Core {
function dummy {
$this->Core->load("Mail");
$this->Mail->send("TITLE", "MESSAGE", "JON@DOE.COM");
}
}
The important part, as to why we link all modules back to the core:
-
$_CORE->DB
can access the core. -
$_CORE->DB
can also access sibling modules. - No longer bound by "hierarchical OOP".
- Develop a module once, it can be reused by all other modules.
- No more "you have to load 999 different parent classes and traits" bloat.
(PART 3) CONSTRUCTORS & DESTRUCTORS
class DB {
public $pdo = null;
public $stmt = null;
function __construct ($core) {
parent::__construct($core);
$this->pdo = new PDO(
"mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
DB_USER, DB_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
}
function __destruct () {
if ($this->stmt!==null) { $this->stmt = null; }
if ($this->pdo!==null) { $this->pdo = null; }
}
}
- Constructor and destructors are often overlooked by beginners, but they are VERY useful.
- In this database class, we automatically connect to the database on
new DB()
. - When the object is destroyed, the destructor closes the connection.
(PART 4) EVIL "PARAMETERS MAPPING"
NO!
// LET'S SAY WE SUBMIT A FORM TO SAVE A USER
$_CORE->load("User");
$result = $_CORE->User->save($_POST["name"], $_POST["email"], $_POST["password"], $_POST["id"]);
Works. But it gets VERY painful if you have to "map parameters to function" a million times.
EVIL EVAL TO THE RESCUE
class CoreBoxx {
function autoCall ($module, $function, $mode="POST") {
// (A) LOAD MODULE
$this->load($module);
// (B) GET FUNCTION PARAMETERS
if ($mode=="POST") { $target =& $_POST; } else { $target =& $_GET; }
$reflect = new ReflectionMethod($module, $function);
$params = $reflect->getParameters();
// (C) EVIL MAPPING
$evil = "\$results = \$this->$module->$function(";
if (count($params)==0) { $evil .= ");"; }
else {
foreach ($params as $p) {
// (C1) POST OR GET HAS EXACT PARAMETER MATCH
if (isset($target[$p->name])) { $evil .= "\$_". $mode ."[\"". $p->name ."\"],"; }
// (C2) USE DEFAULT VALUE
else if ($p->isDefaultValueAvailable()) {
$val = $p->getDefaultValue();
if (is_string($val)) { $evil .= "\"$val\","; }
else if (is_bool($val)) { $evil .= $val ? "true," : "false," ; }
else { $evil .= ($val===null ? "null," : "$val,"); }
}
// (C3) NULL IF ALL ELSE FAILS
else { $evil .= "null,"; }
}
$evil = substr($evil, 0, -1) . ");";
}
// (C4) EVIL RESULTS
eval($evil);
return $results;
}
}
// YES!
$_CORE->autoCall("User", "save");
"Experts", cringe all you want. I am lazy.
(PART 5) "UNIVERSAL" ERROR HANDLER
class CoreBoxx {
function ouch ($ex) {
// SAVE $EX TO FILE? DATABASE?
// AUTO SEND EMAIL ON CRITICAL STOPS?
// SHOW YOUR OWN CUSTOM ERROR MESSAGE
}
}
function _CORERR ($ex) { global $_CORE; $_CORE->ouch($ex); }
set_exception_handler("_CORERR");
- Last bit, we can do a "global catch" in PHP (for all uncaught exceptions).
- Use this to customize your "blue screen of death".
- Keep a log of errors, or even send a warning message on critical failures.
(EXTRA) THE END
That's all for this compressed sharing session.
- Of course, there is no such thing as a "perfect system".
- Constructive critism welcomed. Feel free to disagree with my funky coding.
- Yep, Core Boxx is a "real PHP framework".
Top comments (0)