dynamically loading function code into class - php

I am trying to write class code that can dynamically load external function code
to use as methods of the class. I understand that I could use extends and create
child objects, but it seems there would be a simpler way of going about this.
The point is to limit to bare essentials the amount of code that has to be
used to perform a particular function. And keep all the function code in separate files
that can be required.
class _DEMO
{
public $_addOn = '';
public function __construct()
{
require_once('DEMOAdd.php');
$this->addOn = addOn;
}
}
In DEMOAdd.php:
function addOn($_input)
{
return 'getting '.$_input;
}
How would I load this into the _DEMO class? The above produces fatal error.
print $_test->addOn('something');
In JavaScript it is possible to assign a function to a variable. I realize this
is not JavaScript, but I suspect there is analogous functionality in PHP.
I am also aware of variable variables in PHP but I am having trouble wrapping my brain around that concept.
In response to this advice
When another developer looks at your code it is just not clear what the particular role of this class is. Instead he has to inspect many files and external functions referenced in the class. It just would be better if he could take a look at the class and see "oh, that class does this and that". That's even a central aspect of object oriented programming.
I agree with you on this. I am working on a project that I am the only developer of everything, html, javascript, css,
and php. I have been using class definitions lately, but have written lots of procedural code in the past. The current
project has code that is sectioned off into several class def files so code can be reused in different contexts. My
consideration is if a query from a web page only needs 500 lines of code, why load 1500 lines of code to service the
request. Additionally, this is a cms system with a very limited amount of users (only me at present), so the volume
of traffic is not an issue. But if there was a larger user load, for each user extra code is required, thus the amount
of server memory require increased significantly. The cms web interface is to manage content that direct revision of code
would be very difficult. Yes, I am reinventing the wheel. So has Firestone, General, Nokian, etc etc etc.
Thank you all for the answers
I think I have the answer in anonymous functions that can be assigned to variables, re the php manual
$_var = function($_input)
{
return "getting ".$_input;
}
print $_var('something'); // I have to look back at the manual to varify this line's syntax... yep it looks good.

By using inheritance. You can extend the _DEMO class like this:
class _DEMO_ADD extends _DEMO {
...
function addOn(....) {
...
}
See docs on extends.

Designing the application properly
First of all. What you're trying to do violates fundamental aspects of clean object oriented design. Why even use a class when it's only purpose is to call an external function? If you want to proceed with it stick to functional programming.
The point is to limit to bare essentials the amount of code that has to be used to perform a particular function. And keep all the function code in separate files that can be required.
The attempt of good design is not (only) the amount of code you write, but rather how a certain task is achieved. If a class is suitable for solving your problem then use a class.
When another developer looks at your code it is just not clear what the particular role of this class is. Instead he has to inspect many files and external functions referenced in the class. It just would be better if he could take a look at the class and see "oh, that class does this and that". That's even a central aspect of object oriented programming.
Okay. Enough for off-topic. Since you ask especially for that certain problem I'll give you a few advices on that altough I don't recommend it and would advise to rethink your design.
Dynamic method calls
To call a function that has been defined somewhere else we will make use of the magic method __call($methodname, $args). This method will be called whenever you try to access classes method that does not exists.
Within this __call() method we will check if a function with the name of the method exists, we were trying to call. If not we can throw an Exception, otherwise we're going to execute the function using call_user_func_array().
functions.php
<?php
function addOn($input){
echo "This function is procedural and takes the param: " . $input;
}
MyClass.php
<?php
class MyClass{
// This will get called whenever a method on this class
// is called that does not exist
public function __call($name, $args)
{
// Let's check if a function called like the method we wanted
// to call exists somewhere
if(!function_exists($name))
{
// No? So let's raise an Exception
throw new Exception("The function you called " . $name . " does not exist");
}
// Such a function seems to exist so call it and pass
// the arguments
call_user_func_array($name, $args);
}
public function __construct()
{
// Call undefined method
$this->addOn('My Input');
}
}
index.php
<?php
include 'functions.php';
include 'MyClass.php';
$cls = new MyClass();

Here it is, but with some limitations though...
<?php
class SomeClass
{
private $_funcs = array();
public function setFunc($name,$func)
{
$this->_funcs[$name] = $func;
}
public function __call($name, $param)
{
if(!function_exists($name))
{
if(isset($this->_funcs[$name]))
$this->_funcs[$name]();
else echo "NO $name <br />";
}
}
public function dummy()
{
echo 'DUMMY<br />';
}
}
Then let's test this:
$sc = new SomeClass();
$sc->haha();
$sc->dummy();
$sc->setFunc('foo',function(){echo 'Hello New World!';});
// blah blah blah...
// blah blah blah...
// blah blah blah...
// blah blah blah...
// blah blah blah...
$sc->foo();
One of the limitation that I thought of is how to determine if you want to return a result from $sc level or not.
The result should be, based from the code above:
NO haha
DUMMY
Hello New World!
NOTE: I know this is not the best practice, OP, you should know it too. I just answered OP's question.
EDIT: To be able to determine if the function should return something, try taking advantage of the function name like r_sum with a prefix of r_ to determine that it needs to return something.
Example:
public function __call($name, $param)
{
if(!function_exists($name))
{
if(isset($this->_funcs[$name]))
{
if(strpos($name, 'r_')===0)
return $this->_funcs[$name](); // execute and return
else
$this->_funcs[$name](); // just execute
}
else echo "NO $name <br />";
}
}
But now, the other issue of this that I almost forgot, you cannot access the class properties via, new funcs.
SUGGESTION
Group your similar or related functions together into one static class so you can reuse your codes anytime anywhere.
//APP contains functions designed closely to work with your CMS
// example
APP::abort();
// HTML contains functions like getting page title, external reso, etc
// example
echo HTML::getTitle();
// ROUTE contains many routing reqs that you may need
// example
ROUTE::to('heaven');
// many more...

Related

Creating an MVC using good practices, and making it testable

I recently asked my girlfriend to teach me how to create a website using PHP and if possible to make me understand frameworks. “Of course” she replied helpfully, and gave me some links and books to help me understand the basics of how it works, and what PHP is.
In my web-grazing I visited many sites, including this one (which appeared whenever I had a serious question not relating to cats) and learned about many things including, but not limited to, classes, autoloading, database connections, evil singletons, and design patterns.. not all of which I totally grasped, but at least I wasn’t like ’uh, what?’ when she mentioned them.
We then sat down and she walked me through the getting started phase of creating my own MVC framework.
We created the following folder structure: (abridged):
app/core/
app/controllers/template/
and routed all requests via index.php, to the router class and on the way loaded an autoload class that registered the classes we will use on the site (currently only Template - in controllers/template/template.php - which only echos ‘Hello Rufferto’)
It was at this point that she told me that she liked to create a router table in the database, rather than keeping the routing info in a file, and said:
“let’s instantiate a database class in the router class where we will read the table info. I have my database class already in the app/core directory, It’s got all the mysql_connect() stuff already, and we only ever need one connection, so we’ll load it here as a singleton."
I of course immediately grabbed all her stuff, and threw it and her out into the street, shouting "Don't come back you evil harridan! I think the people at stackoverflow will have to take it from here’
This does leave me with a small problem. Everything we had done up to that point was now being called into question, and before I go any further with my plan to create an MVC framework that will end world hunger, create peace throughout the land, and make anyone who helps me with this question totally irresistible to every member of the gender they prefer, I want to make sure I haven’t started off with all the wrong ingredients.
I have used my most newly acquired skill - pasting - to put the code so far below, and wish some help with the black clouds hanging over the code, which I have put, as I see them, below said code....
index.php:
<?php
require_once(__DIR__ . '/app/config.php');
\APP\Core\Router::create();
config.php:
require_once 'core/autoloader.php';
autoloader.php:
<?php
namespace APP\Core;
class Autoloader
{
public static $loader;
public static $namespaces = array(
'APP\Core' => array('app/core'),
'APP\Models' => array('app/models'),
'APP\Controllers' => array('app/controllers')
);
public static function init()
{
if (self::$loader == null) {
self::$loader = new self();
}
return self::$loader;
}
public function __construct()
{
spl_autoload_register(function ($class_name) {
foreach (Autoloader::$namespaces as $current_namespace => $paths) {
//iterate through each namespace and add the classes - code removed for brevity - it working fine!
}
});
}
}
Autoloader::init();
router.php:
<?php
namespace APP\Core;
class Router
{
function __construct()
{
// crazy ex girfriend instantiated databse here, ive moved it since i want to use PDO
}
public static function create($path='')
{
$cl=get_called_class();
$class = new $cl;
if(empty($path)) $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$query = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
$db_opt = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$db = new \PDO(STD_PDO_DSN, DB_USER, DB_PASSWD, $db_opt);
$sql = "SELECT * FROM `router` where (regex=1 and ? REGEXP `source`) or (regex=0 and `source` = ?) order by `order` asc";
$stmt = $db->prepare($sql);
$stmt->execute([$path, $path]);
$redirect = $stmt->fetch();
if($redirect){
// code here just makes params array from the table - stuff like a body class, meta title etc
$class_redirect::create($params)->run();
}
}else{
echo 'you're an idiot, and should just flip burgers instead';
}
}
}
template.php:
<?php
namespace APP\Controllers\Template;
class Template
{
public function process_some_action()
{
// Like dealing with a post containing new baby names
}
public function process_default()
{
// Do incredible default stuff like list baby names
}
public static function create($params=array())
{
$class = get_called_class();
return new $class($params);
}
function run($params=array())
{
$processed=false;
$processed=$this->call_if_exists("process_some_action");
if(!$processed){
$processed=$this->call_if_exists("process_default");
}
if($processed){
$this->view();
} else {
echo 'Oh ffs Groo';
}
}
function call_if_exists($method_name)
{
if(method_exists($this, $method_name)){
$this->$method_name();
return true;
} else {
return false;
}
}
function view()
{
echo '<br/>';
echo 'Hello Rufferto!';
echo '<br/>';
}
}
1: am I calling the autoloader correctly - is it ok to put it in the ‘config’ file, or should I be putting it somewhere else?
2: er, is it a singleton? If so how can I fix that?
3: I want to use PDO, and have seen a couple of posts directing the questioner to ‘get rid of the useless database class…. But surely what I am looking for here is a ‘class’ that contains the code to make a connection, and have all the controllers or models that need it just call it (and if it doesn’t exist already create it)?
4: I am not convinced I understand the point of the 'create()' function in template (or router for that matter)....
Of course there may be other things wrong, and feel free to point them out to me, but these 4 things are the root of my issue, as I have been going round in circles, renaming things, getting into a state because I’m no longer sure if things are singletons, calling classes that wrap the PDO stuff, then scrapping it all and starting again.
No, there’s no models or views …. I’m not there yet (I will almost certainly use xslt unless the internet breaks with all the stackers shouting at me that it’s moronic) and 1 last thing…… I am not yet comfortable enough with my understanding of dependency injection to use it in any sentence other than this one.
P.S. Absolutely the 1 thing I will never do is call the ex and tell her I’m stuck…….
P.P.S If you have got this far, here’s a treat: http://img1.joyreactor.com/pics/post/comics-threepanelsoul-programmer-job-653145.png
This question would be better suited for codereview. SO is more for "this code doesn't work, what did I do wrong?" questions, rather than "this code works, but can it be improved or am I even doing the right thing?" type questions.
Having said that:
Usually, config files are just that: files containing configuration details. Think database credentials, API keys for third party resources, that sort of thing. You should NOT instantiate classes there. Most full frameworks include a Bootstrap class that handles that sort of thing: initialising the application, loading the config file, autoloader and routes, etc.
Your autoloader is currently a static class with only static methods. That's one way of making a singleton. There's nothing really wrong with that, because you'll only ever want one autoloader in your application.
I suggest you look into some ORM-type libraries, like Doctrine. I've just started work on a project that uses CakePHP's ORM library. If you want to build it yourself, I recommend writing an abstract base model class that has all the logic for retrieving, saving and deleting entries from the database, where each model class itself has a few properties that are unique per model (such as the database table name, for example) and extends this base model.
Those ::create() functions would be useful in a singleton, where they return the existing object if it has already run or instantiate a new one if not, (see example below). Since you seem to have a dislike for singletons, you might want to think about dropping the create() method and just doing the initial setup in the constructor.
Additionally, I wouldn't recommend XSLT for templating. It's a pain, because you'll have to make sure all your controllers build an XML file for your content. If you're going to use templates, look into existing template engines like Twig or Smarty.
Here's that example I promised about using a create() function for a singleton:
class Template {
private static $instance;
public static function create($params = array()) {
if (self::$instance !== null) {
return self::$instance;
}
self::$instance = new static($params);
// do additional setup for the new instance here
return self::$instance;
}
}
Although I'd personally use the name get() instead of create().

optional dependencies within a class

I'm looking for some direction regarding the following, I'm new to OOP and getting there but think either my lack of understanding is causing me to get stuck in a rabbit hole or I'm just over thinking things too much and being anal.
basically i have a main class called "CurlRequest" which sole purpose is to perform curl requests, providing a url and params it returns me some html. This class works and functions as intended and I'm happy with that.
I use this class for a few projects but for one I then wanted to track the performance of my requests made. attempted, failed, passed etc, so i created a static class for this which manages all my counters. I place counter references like the following at different areas in my CurlRequest class.
PerformanceTracker::Increment('CurlRequest.Attempted');
PerformanceTracker::Increment('CurlRequest.Passed');
PerformanceTracker::Increment('CurlRequest.Failed');
I have around 10 or so of these with my class tracking all kinds of things during the curl request and i also use my PerformanceTracker class in other classes i made.
However like mentioned i only wanted to do this for one of my projects, so find my self in the situation of having my original CurlRequest class and an altered one with performance counters in it.
My question is, is their a way i can use the same class for any project and choose to use the PerformanceTracker class or not. The obvious way i thought of was to pass an $option argument into the class and then have if statements around all the counters, but can't help think its messy.
if ($this->options['perfCounter'] == true ) {
PerformanceTracker::Increment($this->owner . '.CurlRequest.Failed');
}
this also adds a lot of extra code to the class.
I suggest placing the if statement in a separate method
private function handlePerformanceTracker($q)
{
if ($this->options['perfCounter'] == true ) {
PerformanceTracker::Increment($q);
}
}
And call this method instead of your calls to
PerformanceTracker::Increment(...);
Also if you find that you want to track performance differently between your projects it might be useful to change your constructor to accept a callable argument, this way you externalize the actual implementation from the CurlRequest class itself.
public function __construct(..., callable performanceHandler)
Then when you instantiate your class:
$curlRequest = new CurlRequest(..., function($outcome) {
//your implementation
});
You can use inheritance and create a subclass that performs the logging before delegating to the parents methods:
class PerformanceTracker
{
static function Increment($s)
{
echo $s;
}
}
class CurlRequest
{
function get($url){
//preform curl request, save html to variable etc
//dummy vars used here so working example code
$html = 'html here';
$curlError = false;
if($curlError){
$this->error($curlError);
}
return $this->success($html);
}
protected function success($html)
{
return $html;
}
protected function error($curlError)
{
throw new Exception($curlError);
}
}
class LoggingCurlRequest extends CurlRequest
{
function get($url)
{
PerformanceTracker::Increment('CurlRequest.Attempted');
return parent::get($url);
}
function success($html)
{
PerformanceTracker::Increment('CurlRequest.Passed');
return parent::success($html);
}
function error($curlError)
{
PerformanceTracker::Increment('CurlRequest.Failed');
parent::error($curlError);
}
}
$lcr = new LoggingCurlRequest();
$lcr->get('unused in example');
As i have used dummy classes with minimal code to demo the technique the benefit might not be obvious, but in you real code, the methods in the CurlRequest class will be more complex, but the methods in the logging class will remain as two liners, with the log function and the call to the parent method.
Using this technique you can modify the parent class without effecting the derived classes (provided the method signatures dont change), can create other derived classes (how about a CachingCurlRequest) etc.
For the full benefits of OOP you should look into dependency injection and interfaces
From an OOP perspective you could use the 'Null' object pattern. This just means that the dependency used by the CurlRequest class is abstract (possibly an interface?). You would then have Two concrete implementations of PerformanceTracker: the one you have today and one that does nothing (it does not have any behavior). In this way for the one project when you instantiate the CurlRequest class it would use the concrete implementation that has behavior and for all the other projects it would use the concrete implementation with no behavior. All of the code in CurlRequest would look the same but it would have different behavior depending on which concrete implementation it was using

Using PHPUnit to test helper functions

Let's say I want to test a simple helper that takes a class name as an argument and makes a redirection.
How am I supposed to test this if the function is called in many places from inside a couple of controllers? Should I test every class name that was passed as a parameter in the whole code (write them in the provider function myself)? Or is there a magical function which does that for me?
Your question is the exact reason why dependency injection -- when done correctly (not how most popular frameworks "implement" it) -- is touted as the ultimate in code testability.
To understand why, lets look at how "helper functions" and class-oriented programming make your controllers difficult to test.
class Helpers {
public static function myHelper() {
return 42;
}
}
class MyController {
public function doSomething() {
return Helpers::myHelper() + 100;
}
}
The entire point of unit testing is to verify that "units" of code work in isolation. If you can't isolate functionality, your tests are meaningless because their results could be tainted by the behavior of the other code involved. This can result in what statisticians call Type I and Type II errors: basically, this means you can get test results that might be lying to you.
In the code above, the helper cannot be easily mocked to determine that MyController::doSomething works in complete isolation from outside influences. Why not? Because we can't "mock" the behavior of the helper method to guarantee our doSomething method actually adds 100 to the helper result. We're stuck with the helper's exact behavior (returning 42). This is a problem that correct object-orientation and inversion of control eliminate entirely. Let's consider an example of how:
If MyController asks for it's dependencies instead of using the static helper function , it becomes trivial to mock the outside influences. Consider:
interface AnswerMachine {
public function getAnswer();
}
class UltimateAnswerer implements AnswerMachine {
public function getAnswer() {
return 42;
}
}
class MyController {
private $answerer;
public function __construct(AnswerMachine $answerer) {
$this->answerer = $answerer;
}
public function doSomething() {
return $this->answerer->getAnswer() + 100;
}
}
Now, it's trivially simple to test that MyController::doSomething does in fact add 100 to whatever it gets back from the answer machine:
// test file
class StubAnswerer implements AnswerMachine {
public function getAnswer() {
return 50;
}
}
$stubAnswer = new StubAnswerer();
$testController = new MyController($stubAnswerer);
assert($testController->doSomething() === 150);
This example also demonstrates how the correct use of interfaces in your code can greatly simplify the testing process. Test frameworks like PHPUnit make it very easy to mock interface definitions to perform exactly what you want them to in order to test the isolated functionality of code units.
So I hope these very simple examples demonstrate how powerful dependency injection is when it comes to testing your code. But more importantly, I hope they demonstrate why you should be wary if your framework of choice is using static (just another name for global), singletons, and helper functions.
You cannot test each possible combination of parameters to all the functions you need to test; it will take you longer than the life of the universe. So you use Human Intelligence (some might call it cheating ;-). Test it just once, in this case with a mock controller as the parameter.
Then look at your code and ask yourself if any other object passed in is really going to have it behave differently. For something you describe as a "simple helper" maybe the answer is no. But, if yes, how? Create another mock controller class that simulates that different behaviour. E.g. this second controller might not have the function your helper class expects to call. You expect an exception to be thrown. Create the unit test for that.
Repeat until satisfied.

PHPUnit mock/stub functionality within class

I need some advice on how I can proceed with this issue.
Using PHP
An example would be:
class BuilderClass {
function getClass($id, $some, $vars){
$dbResult = new db_Class::getDbRows($id, $some, $vars);
foreach(...)
// Build something from the database values
return self;
}
}
So what I want to do is to create a test case where I somehow mock the db results.
I have not found any great way to do this, please point me in the right direction or similar to get this working for me.
I could change something within the builder itself for example call a class that runs the function: FunctionRunner::runStaticFunction("db_Class", "getDbRows", $args, $something_else); But at the moment I don't know if that is possible neither. Any research articles that cover this or any sites that explain this. I'd appriciate anything at the moment.
Thanks
/Marcus
Split the operations of retrieving data from database, and building the data.
class BuilderClass {
function getClass($id, $some, $vars){
$dbResult = new db_Class::getDbRows($id, $some, $vars);
return doGetClass($dbResult);
}
function doGetClass($dbResult) {
foreach(...)
// Build something from the database values
return self;
}
}
That way, you can test doGetClass in isolation from calling the database .
As often the case, inability to easily write tests for your functions is caused by a flaw in your application design. In this case the db_Class is tightly coupled to your BuilderClass.
A proper solution would be to have a Database object in your BuilderClass using dependency injection, and mocking that injection to return a static result.
class BuilderClass
{
protected $oDatabase;
public function __construct(db_Class $oDatabase) {
$this->oDatabase = $oDataabse;
}
public function getClass($someVars) {
$this->oDatabase->getDbRows($someVars);
}
}
This way, the Database object is easily replaced with a stub.
There are many ways to do this, but since we are talking PHP, you could leverage the magic class loader function.
Simply put, if you want to mock the data access layer, you just create an object with the actual name of the data class, and the autoloader is never called.
Want to actually access the database? don't define the class and the autoloader will be called when something tries to access the database, which should then know what to do to load the class.
Mostly my autoloaders, when I use them, tend to look something like this;
function __autoload($className)
{
if(file_exists('../includes/'.$className.'.php'))
require_once('../includes/'.$className.'.php');
}

PHP: Callback on Entry/Exit of Class Methods?

Is there a way I can set up callbacks on (or automataically log) method parameters, entries, and exits without making explicit calls within each method? I basically want to log this information to my logger class (which is static) without having to do it manually for each method.
Right now I have to call Logger::logEntry() and Logger::logExit() in every method to accomplish this. I would love to not have to do this:
class TestClass {
public function tester($arg) {
Logger::logEntry();
Logger::info('Parameter $arg => ' . $arg);
// Do some stuff...
Logger::logExit();
}
}
use a wrapper class. this method has the following benefits:
no need to change your underlying class structure / method signatures
change logging? just update this class
update object calls vs inserting code into every class you want to log
.
class LogWatch {
function __construct($class) {
$this->obj = $class;
}
function __call($method, $args) {
if (in_array($method, get_class_methods($this->obj) ) ) {
Logger::logEntry();
Logger::info('Parameter '.implode(', ', $args) );
call_user_func_array(array($this->obj, $method), $args);
Logger::logExit();
} else {
throw new BadMethodCallException();
}
}
}
$test = new LogWatch(new TestClass() );
$test->tester();
// you can use instances of `LogWatch()` just like your watched class
// including passing appropriate params:
$test->tester($param1, $param2);
If you want to do function logging for the sake of debugging you may want to look into the Xdebug extension. There's no good way to intercept function calls at at runtime, and any automated interception will add great runtime overhead.
Using XDebug you could instead turn it on as-needed, as well as get lots of other stuff
( XDebug is thus used with PHPUnit to do unit testing and coverage analysis. )
Xdebug
PHPUnit
The Problem with __call
__call may look to be a fun solution to the problem, but there are 3 problems with this, namely
Significant Execution Overhead. your doing __call --> call_user_func_array , which will literally add not one, but two function calls to every execution.
Backtraces become indecipherable: The actual function you were trying to call gets lost in a sea of __call and call_user_func_array making backtracing exceedingly hard, especially if your backtraces come with their arguent lists included.
Stupid Hidden Functions: You're going back to PHP4 style "hiding" of functions by prefixing them with _ to stop the user directly calling it or seeing it, because if the function name happens to be named what they wan't, the __call wont trigger, so you have already got a whole class full of really horrible function names, which developers will be tempted to call directly anyway in various places. ( And if you want to get rid of __call later, you will have to rename all these functions as to not break the code! )
Thus, if you are utilising php code to implement this will result in epically horrible code, that any future user of your codebase will NOT want to work with. You are far better getting something ( like Xdebug ) that can be added transparently when you need it, and save greatly polluting your code.
you could use the magic function __call. It gets called when no functions match that name. Rename your methods to be prefixed with something, (eg: underscore), and optionally set them to private/protected.
class TestClass {
public function __call($function, $args) {
Logger::logEntry();
Logger::info('Parameters: ' . implode(", ", $args);
$localFunc = "_" . $function;
$return = $this->$localFunc($args);
Logger::logExit();
return $return;
}
private function _tester() {
// do stuff...
return "tester called";
}
}
$t = new TestClass();
echo $t->tester();
// "tester called"

Categories