DEV Community

Cover image for Lazy and Unorthodox PHP Coding
Code Boxx
Code Boxx

Posted on

Lazy and Unorthodox PHP Coding

  • 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();
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode
  • 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]; }
  }
}
Enter fullscreen mode Exit fullscreen mode
  • "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 {}
Enter fullscreen mode Exit fullscreen mode
  • Take note, $_CORE->modules["DB"] = new DB($_CORE). Yes, we pass $_CORE into new DB().
  • What happens here:
    • $_CORE->DB->Core =& $_CORE
    • Same magic __get() trick. In all functions of DB, $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");
  }
}
Enter fullscreen mode Exit fullscreen mode

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; }
  }
}
Enter fullscreen mode Exit fullscreen mode
  • 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"]);
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

"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");
Enter fullscreen mode Exit fullscreen mode
  • 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)