Skip to content

Instantly share code, notes, and snippets.

@jtickle
Last active December 20, 2015 23:39
Show Gist options
  • Save jtickle/6213838 to your computer and use it in GitHub Desktop.
Save jtickle/6213838 to your computer and use it in GitHub Desktop.
Functional PHP: A getModule function that works like Node.js' require()
<?php
// I have no idea how to solve the problem of the fact that anything
// declared here ends up on the global scope. Namespaces, maybe? But
// I'm doing this in part to get away from namespaces and just let things
// be functional. So I wrap it up in this load function but you should
// try to pick some name that won't conflict.
function fancyApi(&$exports) {
$myConfig = 'World';
// This fancy API simply prints a familiar message given the right inputs
$exports['sayHello'] = function($hello) use ($myConfig) {
echo "$hello, $myConfig!\n";
};
}
fancyApi($exports);
?>
<?php
// This feels dirty
define('DS', '/');
// We have to initialize it with configuration. Your software could just
// declare getModule as a non-anonymous function if you really wanted.
function createGetModule($basePath) {
// We will store our loaded modules here. Each call to createGetModule
// gets its own module cache. It's Functional Programming, and that's
// how it goes.
$loaded = array();
// Return the actual getModule function
return function($path) use ($loaded, $basePath) {
// If a load hasn't happened before, we need to go do the actual load.
if(!array_key_exists($path, $loaded)) {
// Build the path - this could be configurable with more stuff passed
// into the outer function createGetModule
$file = $basePath . DS . $path . '.php';
// Sanity checking because I like Exceptions better than Errors
if(!file_exists($file)) throw new Exception($file . ' does not exist.');
// The module just expects that this array will be available to it.
// Everything the module exports will be put onto $exports.
$exports = array();
// Load the module in the PHP way
require $file;
// Cache the results. Modules are singletons.
$loaded[$path] = $exports;
}
// Return the result from cache
return $loaded[$path];
};
}
?>
#!/usr/bin/php
<?php
// We have to bootstrap things the old-fashioned way
require('getModule.php');
// Assign the getModule behavior to a variable. Note that createGetModule returns a function.
$getModule = createGetModule(__DIR__);
// Use getModule to load fancyApi's exports
$api = $getModule('fancyApi');
echo "Here is what we get back:\n";
var_dump($api);
echo "\n\nAnd here is what happens if we run it:\n";
$api['sayHello']('Hello');
echo "\nUnfortunately, that function declared in fancyApi.php polluted the global scope:\nis_callable('fancyApi') = ";
var_dump(is_callable('fancyApi'));
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment