Core Boxx – PHP Modular Development Framework

Core Boxx is a lightweight PHP modular development framework. The whole idea is not to be another overly bloated framework with tons of unused features. So it is built around the idea of modularity, load only what you need. It is more like “predefined system modules you can expand on”, rather than a full-fledged framework.

 

TABLE OF CONTENTS

Download & Notes Quickstart Tutorial Quick Reference

 

 

DOWNLOAD & NOTES

First, here are the download links and a quick “setup guide” for the impatient folks who don’t have the patience to read through everything.

 

REQUIREMENTS

  • Apache Mod Rewrite
  • PHP PDO Extension
  • Best to have at least PHP 7.4

Core Boxx has not been extensively tested, but it is developed and works on a WAMP8 (Windows Apache MySQL PHP 8) server.

 

LICENSE & DOWNLOAD

Core Boxx is released under the MIT License. You are free to use it for your own personal and commercial projects, modify it as you see fit. On the condition that there the software is provided “as-is”. There are no warranties provided and “no strings attached”. Code Boxx and the authors are not liable for any claims, damages, or liabilities.

Download Core Boxx | GitHub | Source Forge

 

 

CORE BOXX MODULES

Go ahead, check out the rest of the Core Boxx modules. Download the ones you need, plug them into Core Boxx. Do some of your own customizations, and help yourself speed up development.

  1. Mail – Send emails with ease.
  2. Resumable Upload – Resumable uploads with FlowJS and FlowPHP.
  3. Dynamic Menu – Create a dynamic HTML menu with a database.
  4. Dynamic Content – Store and retrieve contents from the database.
  5. Users – User database and management functions.
  6. Forgot Password – An “extension” of the user module, automated password recovery.
  7. One Time Password – For added security.
  8. Pretty URL – Turn an ugly URL like http://site.com/hello-world-123.php to http://site.com/hello-world/.
  9. Likes & Dislikes Reaction – Or was it “upvote and downvote”?
  10. Comments – Allow registered users to comment on your posts/products/images/videos.

 

QUICKSTART TUTORIAL

Don’t worry, Core Boxx does not have some crazy documentation that takes months to read. Just this page. How can it be used to help develop web applications quickly? Let us walk through a quick example of creating a user module (click here to download all the tutorial code).

 

QUICK OVERVIEW

First, there are only 2 folders in Core Boxx.

  • lib Library files should be kept in this folder.
  • api The API endpoint to handle AJAX calls, mobile app calls, server-to-server communications.

Then, there are only 3 “base library files” in the lib folder.

  • GO.php The “fire starter”, contains the settings and stuff. Simply include this in your own pages to use Core Boxx.
  • LIB-Core.php The core library, where the magic starts.
  • LIB-DB.php Default MYSQL PDO database module.

 

 

STEP 1) CHANGE THE SETTINGS

lib/GO.php
// (A2) HOST
define("HOST_BASE", "http://localhost/"); // CHANGE TO YOUR OWN!
 
// (A3) DATABASE SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8");
define("DB_USER", "root");
define("DB_PASSWORD", "");

// (B4) START SESSION - ENABLE SESSION FOR THIS EXAMPLE
session_start();

Captain Obvious step 1, change the settings to your own. Feel free to tweak the rest if required… If you are having trouble with the auto path, want to change how error reporting is done, or want to start the session by default.

 

STEP 2) CREATE DATABASE TABLES

users.sql
CREATE TABLE `users` (
  `user_id` int(11) NOT NULL,
  `user_email` varchar(255) NOT NULL,
  `user_password` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 
ALTER TABLE `users`
  ADD PRIMARY KEY (`user_id`),
  ADD UNIQUE KEY `user_email` (`user_email`);
 
ALTER TABLE `users`
  MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;CODE

Next, we create the database tables (if any). This is just a simple dummy users table with only 3 fields:

  • user_id The primary key.
  • user_email The user’s email, unique field.
  • user_password Self-explanatory.

 

 

STEP 3) DEVELOP THE LIBRARY MODULE

lib/LIB-Users.php
<?php
class Users extends Core {
  // (A) SAVE USER
  // $email : user email
  // $pass : user password
  // $id : user ID (for updating only)
  function save ($email, $password, $id=null) {
    // (A1) DATA SETUP
    $fields = ["user_email", "user_password"];
    $data = [$email, $password];
 
    // (A2) NEW/UPDATE USER
    if ($id === null) {
      return $this->DB->insert("users", $fields, $data);
    } else {
      $data[] = $id;
      return $this->DB->update("users", $fields, "`user_id`=?", $data);
    }
  }

  // (B) GET USER
  // $id : user id or email
  function get ($id) {
    return $this->DB->fetch(
      "SELECT * FROM `users` WHERE `user_". (is_numeric($id)?"id":"email") ."`=?", [$id]
    );
  }

  // (C) VERIFY
  // $email : user email
  // $password : user password
  function verify ($email, $password) {
    // (C1) GET USER + VERIFY PASSWORD
    $user = $this->get($email);
    if ($user===false) { return false; }
    $pass = is_array($user);
    if ($pass) { $pass = $password == $user['user_password']; }

    // (C2) RESULTS
    if (!$pass) {
      $this->error = "Invalid user/password.";
      return false;
    } else { return true; }
  }
 
  // (D) GET ALL USERS
  // $search : user email
  // $page : current page
  function getAll ($search=null, $pg=1) {
    // (D1) INIT SQL
    $sql = "FROM `users`";
    if ($search!==null) { $sql .= " WHERE `user_email` LIKE ?"; }
    $data = $search==null ? null : ["%".$search."%"];
 
    // (D2) PAGINATION
    $entries = $this->DB->fetchCol("SELECT COUNT(*) $sql", $data);
    if ($entries===false) { return false; }
    $page = $this->core->paginator($entries, $pg);
 
    // (D3) FETCH
    $users = $this->DB->fetchAll("SELECT * $sql", $data, "user_id");
    if ($users===false) { return false; }
    return ["data" => $users, "page" => $page];
  }
}

With the database table in place, the next step is to create the library itself.

  • Since we are creating a user module, we create a corresponding lib/LIB-Users.php – Take note of the capital U.
  • Set the class name to the same as the file name, and remember to extend the core – class Users extends Core.
  • Add the user functions. In this dummy library, we have 4 simple functions:
    • function save() Add or update a user.
    • function get() Get a user by ID or email.
    • function verify() Verify the given email and password (for login).
    • function getAll() Get all or search for users… Pagination is kind of a pain, take some time to digest it.
  • Take note of how we “link back” and use functions from the database library $this->DB->insert(), $this->DB->update(), $this->DB->fetch().
  • If you are curious about how this works, dissect function load() and class Core in lib/LIB-Core.php in your own free time… If not, just know that you can use $this->core->XXX to access the core library.

P.S. Modules can also access each other. For example, we can create another Cipher module. So when saving the user passwords, we can use the encryption function – $this->core->load("Cipher"); $password = $this->core->Cipher->encrypt($password);

 

 

STEP 4) USE THE LIBRARY

user-test.php
<?php
// (A) LOAD CORE + USERS MODULE
require "lib/GO.php";
$_CORE->load("Users");
 
// (B) ADD NEW USER
echo $_CORE->Users->save("john@doe.com", "lousypassword") ? "OK" : $_CORE->error ;
 
// (C) UPDATE USER
echo $_CORE->Users->save("jane@doe.com", "lousypassword123", 1) ? "OK" : $_CORE->error ;
 
// (D) GET BY ID OR EMAIL
$user = $_CORE->Users->get(1);
if ($user===false) { echo $_CORE->error; }
else { print_r($user); }
  
$user = $_CORE->Users->get("jane@doe.com");
if ($user===false) { echo $_CORE->error; }
else { print_r($user); }
 
// (E) GET OR SEARCH USERS
$users = $_CORE->Users->getAll("jane");
if ($users===false) { echo $_CORE->error; }
else { print_r($users); }
 
// (F) VERIFY USER PASSWORD
echo $_CORE->Users->verify("jane@doe.com", "lousypassword123") ? "OK" : $_CORE->error ;

The database and library are in place, all that’s left is to use it in your own pages and scripts.

  • Very simply, require "lib/GO.php" in your own script to start Core Boxx.
  • To load the user module that we just created – $_CORE->load("Users").
  • All the user functions can now be accessed at $_CORE->Users->FUNCTION().

 

EXTRA) LOGIN PAGE

user-login.php
<?php
// (A) PROCESS LOGIN
if (isset($_POST["email"])) {
  require "lib/GO.php";
  $_CORE->load("Users");
  if ($_CORE->Users->verify($_POST["email"], $_POST["password"])) {
    /* DO YOUR LOGIN SEQUENCE
    $_SESSION["user"] = ...;
    header("Location: HOME PAGE");
    exit();
    */
    exit("IT WORKS!");
  } else { $error = true; }
} ?>
 
<!-- (B) LOGIN FORM -->
<?php
if (isset($error)) { echo "<div>{$_CORE->error}</div>"; }
?>
<form method="post">
  <input type="email" name="email" required placeholder="Email"/>
  <input type="password" name="password" required placeholder="Password"/>
  <input type="submit" value="Login"/>
</form>

For the people who might still be lost on “how to use it in an HTML page” – Here is another quick example of a login page.

P.S. Core Boxx is a PHP framework, not an HTML/CSS/Javascript framework. It does not dictate how to build your webpages, nor does it limit which client-side framework you use.

 

STEP 5) DEVELOPING THE API ENDPOINT (OPTIONAL)

Actually, the above steps pretty much covered a fully functioning system. But for the people who are looking to create an endpoint for AJAX or server-to-server calls, here is a simple example.

api/API-user.php
<?php
switch ($_REQ) {
  // (A) INVALID REQUEST
  default: 
    $_CORE->respond(0, "Invalid request", null, null, 400);
    break;
 
  // (B) GET USER BY ID
  // http://site.com/api/user/get/?id=1
  case "get":
    $_CORE->autoGETAPI("Users", "get", "GET");
    break;
 
  // (C) GET OR SEARCH FOR USERS
  // http://site.com/api/user/getAll/?search=jane&pg=1
  case "getAll":
    $_CORE->autoGETAPI("Users", "getAll", "GET");v
    break;
 
  // (D) UPDATE USER
  // http://site.com/api/user/update/?email=foobar%40doe.com&password=123&id=1
  case "update":
    $_CORE->autoAPI("Users", "save", "GET");
    break;
}
  • Take note of the URL format for API calls – http://site.com/api/MODULE/REQUEST/. For example, if we want to get a user, it can be http://site.com/api/user/get/?id=999.
  • In api/index.php, the path segment of the URL will be parsed – $_MOD holds the requested module, and $_REQ holds the requested action.
  • $_MOD will be used to try to find the respective handling script. In this case, http://site.com/api/user/get/ will look for api/API-user.php.
  • The rest should be straightforward, we are simply using switch($_REQ) to handle the various requests accordingly.
  • Lastly, take note of how $_CORE->autoapi() and $_CORE->autoGETAPI can be used to automatically map the data variables to the module function – Don’t need to stick with this though. Feel free to formulate and come up with your own “standard API processes”.

 

 

EXTRA) ERROR HANDLING

RECOMMENDED FOR DEVELOPMENT SERVERS

lib/GO.php
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", 1);
ini_set("log_errors", 0);
define("ERR_SHOW", true);

RECOMMENDED FOR LIVE SERVERS

lib/GO.php
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", 0);
ini_set("log_errors", 1);
ini_set("error_log", "PATH/error.log");
define("ERR_SHOW", false);

BUILD YOUR OWN CUSTOM ERROR HANDLER

Create a lib/LIB-BooBoo.php and a function ouch() inside.

lib/LIB-BooBoo.php
<?php
class BooBoo extends Core {
  function ouch ($ex) {
    // API MODE - OUTPUT JSON ENCODED MESSAGE
    if (defined("API_MODE")) { ... }

    // WEB MODE - SHOW HTML ERROR MESSAGE
    else { ... }
  }
}

Then, load this library by default:

lib/GO.php
$_CORE->load("BooBoo");
$_CORE->load("DB");

 

EXTRA) SUPPORTING CORS FOR API CALLS

lib/GO.php
// define("API_CORS", false); // no cors support
// define("API_CORS", true); // any domain + mobile apps
// define("API_CORS", "site-a.com"); // this domain only
// define("API_CORS", ["site-a.com", "site-b.com"]);

Just enable the API_CORS setting accordingly.

 

QUICK REFERENCE & EXAMPLES

With that, we have come to the end of the “quickstart tutorial”, and here is a summary of the core functions (plus quick examples).

 

ENGINE CORE (LIB-CORE.PHP)

function load($module)

Loads lib/LIB-$module.php, and extends it to $_CORE->$module = new $module();

  • $module – String, module to load.
$_CORE->load("Users");
function loaded($module)

Checks if the specified $module is loaded.

  • $module – String, module to check.
if ($_CORE->loaded("Users")) { ... }
function autoCall($module, $function, $mode)

Automatically map POST or GET variables to the specified module function, and run it.

  • $module – String, module to load.
  • $function – String, function to call.
  • $mode – String, POST or GET. Defaults to POST.
$users = $_CORE->autoCall("Users", "getAll");
function autoAPI($module, $function, $mode)

Automatically map POST or GET variables to the specified module function, and respond after running it.

  • $module – String, module to load.
  • $function – String, function to call.
  • $mode – String, POST or GET. Defaults to POST.
$_CORE->autoAPI("Users", "save");
function autoGETAPI($module, $function, $mode)

Automatically map POST or GET variables to the specified module (get entries) function, and respond after running it.

  • $module – String, module to load.
  • $function – String, function to call.
  • $mode – String, POST or GET. Defaults to POST.
$_CORE->autoAPI("Users", "save");
function random($length)

Creates an alphanumeric string.

  • $length – Integer, string length. Defaults to 16.
$password = $_CORE->random(10);
function respond($status, $msg, $data, $more, $http, $exit)

Formats and outputs a standard JSON encoded string.

  • $status – Boolean, 1, 0, or invent your own set of status code.
  • $msg – String, system message.
  • $data – Data if any.
  • $more – Additional data, if any.
  • $http – Optional, HTTP response code.
  • $exit – Optional, stop processing after JSON string output. Defaults to true.
$_CORE->respond(0, "An error has occurred!", null, null, 500);
function paginator($entries, $now)

Calculates pagination.

  • $entries – Integer, the total number of entries.
  • $now – Integer, the current page number.
$entries = 1234; // DO YOUR OWN SELECT COUNT(*) FROM `TABLE`
$now = 4; // CURRENT PAGE
$page = $_CORE->paginator($entries, $now);
/* $page = [
  "entries" => TOTAL NUMBER OF ENTRIES
  "total" => TOTAL NUMBER OF PAGES
  "now" => CURRENT PAGE
  "x" => USE THIS TO LIMIT X,Y YOUR SQL QUERY
  "y" => USE THIS TO LIMIT X,Y YOUR SQL QUERY
] */

 

DATABASE CORE (LIB-DB.PHP)

function __construct()
Connects to the database when the object is created.
function __destruct()
Closes the database connection when done.
function connect()
Connect to the database.
function start()

Auto-commit off.

$this->DB->start();
$pass = $this->DB->update(SQL 1);
if ($pass) { $pass = $this->DB->update(SQL 2); }
$this->DB->end($pass);
return $pass;
function end($pass)

Used in conjunction with start().

  • $pass – Boolean, commit or rollback?
function query($sql, $data)

Runs an SQL query.

  • $sql – String, SQL query to run.
  • $data – Array, data to feed into query.
$this->DB->query(
  "DELETE FROM `users` WHERE `id`=?", [$id]
);
function fetchAll($sql, $data, $key)

Fetch multiple rows of data.

  • $sql – String, SQL query to run.
  • $data – Array, data to feed into query.
  • $key – String, use this column as the key of the array. Optional.
$users = $this->DB->fetchAll(
  "SELECT * FROM `users` WHERE `age`<?",
  [30], "user_id"
);
function fetch($sql, $data)

Fetch a single row of data.

  • $sql – String, SQL query to run.
  • $data – Array, data to feed into query.
$user = $this->DB->fetch(
  "SELECT * FROM `users` WHERE `id`=?", [$id]
);
function fetchCol($sql, $data)

Fetch a single column of data.

  • $sql – String, SQL query to run.
  • $data – Array, data to feed into query.
$email = $this->DB->fetchCol(
  "SELECT `user_email` FROM `users` WHERE `id`=?", [$id]
);
function insert($table, $fields, $data, $replace)

Insert or replace data into the specified database table.

  • $table – String, target table.
  • $fields – Array, name of fields.
  • $data – Array, data to feed into query.
  • $replace – Boolean, replace instead of insert?
$this->DB->insert("users", 
  ["user_email", "user_password"]
  ["john@doe.com", "123", "jane@doe.com", "456"]
);
function update($table, $fields, $where, $data)

Run update query.

  • $table – String, target table.
  • $fields – Array, name of fields.
  • $where – String, the “WHERE” clause.
  • $data – Array, data to feed into query.
$this->DB->update("table",
  ["user_email", "user_pass"],
  "`user_id`=?",
  ["joy@doe.com", "PASSWORD", 1]
);

9 thoughts on “Core Boxx – PHP Modular Development Framework”

  1. {“status”:0,”message”:”Call to a member function execute() on bool”,”code”:0,”file”:”C:\\xampp\\htdocs\\box\\lib\\LIB-DB.php”,”line”:40}

    I keep getting that, i want to use sqlite as a database.

      1. Thank you! I will do my research for that and try to build it. I want SQLite since my most of the project built with this framework will be small projects or prototypes that I want to move fast from server to server or locally.

  2. Thanks much for this. Found a small typo:
    In the line below, instead of User.php, it should be Users.php:

    Since we are creating a user module, we create a corresponding lib/User.php – Take note of the capital U.

  3. Thanks again for your FREE work. It is helpful & appreciated.

    I am attempting to login via the API from external site, but cannot seem to login via cURL. Although I set CURLOPT_POSTFIELDS, the email & password do not seem to be available as $_POST variables in the session.php script under api folder.
    To try debug, I have tried to force with test data directly, but even then the mysite/api/users/verify returns {“status”:0,”message”:”Please sign in first”,”data”:null,”page”:null}

  4. This seems a very elegant, starting point. Thanks
    Is there a forum or further help facility, for questions not covered here?

Leave a Comment

Your email address will not be published. Required fields are marked *