PHP class and method overriding - implement callbacks - php

I'm currently working on a project where the core system is distributed out to many different clients - and then should the client request changes, we have to make them individually on each system which means that eventually the core code varies from client to client, and keeping it up to date and copying new features across the system is difficult.
I have proposed we move to (what I'm calling) an 'override model' which has an outside skeleton structure of the code. Somewhat like:
|- controllers
|- models
|- views
|- core
|- controllers
|- Controller1.php
|- models
|- views
If you then wanted to make changes to Controller1.php, you would copy it into the outside structure and make changes - an autoloader would then load the appropriate files if they exist by checking the Skeleton structure for them first, i.e.
Loader::controller('Controller1');
However I wondered if it is possible to go a bit further than that - its all well and good overriding the Controller if changes are needed, but then any future core additions or fixes might not get added in. So I thought you could possibly create a copy of the file and override just the singular method calls. An semi-example of what I mean is as follows:
class Override {
public function __call($method, $args) {
return call_user_func_array(array('Something', $method), $args);
}
public static function __callStatic($method, $args){
return call_user_func_array(array('Something', $method), $args);
}
}
// Core class
class Something {
static function doTest() {
echo "Class something <br/>";
}
static function doOtherTest() {
echo "That works <br/>";
self::doTest();
}
}
// Overriding class - named differently for ease of example and reasons explained later
class SomethingElse extends Override {
private static function doTest() {
echo "Success <br/>";
}
}
// Actual function calling
SomethingElse::doTest();
SomethingElse::doOtherTest();
The general idea being that if the method doesn't exist in the originating class, then action it from the 'parent' class (which is hardcoded here). However I have two issues with this method:
I think I will run into trouble when the classes have the same names
If I am attempting to override a method that the parent class subsequently calls, it will use it's own version of the method as opposed to the one I am attempting to override
Although the 'simple' solution is to say you should override any methods that are in conjunction, but more might be added at a later date.
Currently I am trying to just do the initial solution of full class overriding using the loader, which works and is less complex.
However I wondered if any of the great minds on StackOverflow might know of any answer or set-up that might help address the issues with the method overriding idea - please bear in mind I'm working with an existing system set up, although the skeleton structure idea is what I am trying to implement to bring some form of 'control' over what is changed. Ideally, nothing in the core would change (at least not by much) when someone wants to override a method or similar.

Well we've just solved it. Traits it is!
But seriously by converting the versioned code to traits and then calling them in the non versioned files in the above structure. This then negates the need for a loader class and other clash prevention layers and allows the core code to be updated, tested and committed without affecting custom code per client.

well the strict OO Kind of Solution would surely be to spread your controller as abstract interfaces that could be implementet by several different real world approaches and then brought together by the composition over inheritace principle.
As i understand you've got existing code here that you intend to override or extend. If your PHP-Version allows you to use Traits, this might help you too:
PHP 5.4: why can classes override trait methods with a different signature?

Related

Is the current way of building in PHP, the html layout structure possible?

I started not long ago with building my own mvc structure in PHP.
I have seen many people include in home.php page the header.php and footer.
I am currently stuck in finding a solid way to render my views.
I would like to know if it is even possible the way I am combining the header.php, home.php, and footer.php because this is not working for me which made me curious if there is even a native clean way of working with PHP layout structures?
any info would be appreciated. I try my best to explain the code below.
now working with this MVC structure. the router currently checks the req url and gives the controller
example of very basic router:
2 parameters: first the route, second the controller witch should render the view.
public function get($route, $controller) {
if($_SERVER['REQUEST_METHOD'] !== 'GET') {
return false;
}
$uri = $_SERVER['REQUEST_URI'];
if($uri === $route) {
$this->handled = true;
return include (controllers . $controller);
}
}
the routes that are being called:
$router = new Router();
$router->get('/', 'home.contr.php');
$router->get('/home', 'home.contr.php');
$router->get('/about', 'about.contr.php');
$router->get('/portfolio', 'projects.contr.php');
the router calls the controller and in my controller I render the view. with CreateView function
Home.contr.php:
class Home extends Controller {
public function __construct() {
Home::CreateView('home');
}
}
$home = new Home();
the extend controller that should implement the views/layout:
class Controller {
public static function CreateView($viewName) {
require_once views . 'components/header.php';
require_once views . "$viewName.php";
require_once views . "components/footer.php";
}
}
thank you in advance.
Limitations
MVC is a very broad topic with much different understanding so that by the term alone this is hard to answer properly - even in context of a PHP application. You normally refer to an existing implementation of MVC which is not the case here as you want to do it your own (Hint: Read code of existing implementations that is available and about you want to learn more).
Discussion
With that being said, you can find some practical "first next steps" suggestions at the end of the answer.
But I read your question as well that you're concerned about the HTML templates and perhaps also what this has to do with how you wrote your example. So I start a non-binding discussion about the View and then go over to Route and Controller. The Model layer I've kept out of the discussion mainly, at least for that you have to face third-party libraries as otherwise your application structure would not be a good host for broad functionality, this is touched by autoloading.
I have no authority in MVC, I just used some of the early implementations in PHP and applications influenced by them but never implemented it fully. So don't read out any suggestion from the discussion regarding it, it is merely about your example and what came to my mind in specific to PHP. At the end of the day it is you who will find the answer to your own programming questions.
Let's go.
A suggestion/assumption first: You certainly don't want to implement the view creation with the Controller class but with a View class. It would not change much just that the controller does not "care" about it (MVC = Model View Controller).
You can refactor (change) your code by introducing a View class and move the Controller::createView() to View::create() (compare: extract/move method).
Then using require_once - while it may work - it would only work if the template file is only used once. This is certainly not what you want to express here (and later in the discussion we'll see that with the existing example this can also more easily happen than perhaps intentionally thought), instead use require (or include depending on how you want to handle errors) as they will always execute the code in the file (for potential problems redefining controllers, see later in the discussion first routing and then second autoloading).
Apart from obvious code errors (typos) you'd need to address to get it to run (which is a good opportunity to explore PHP error handling and monitoring for your application) you still need to pass the output data of the controller to the view.
This can be so called view models or just objects (in the broader sense) holding the data to be viewed (rendered by the view). Just require/include-ing the (HTML layout) template files won't suffice as they may contain the HTML structure but not the controllers' output data. On the level of the templates this is typically in variables, e.g. the title of the hypertext document:
<title>
<?= htmlspecialchars($title, ENT_QUOTES | ENT_HTML5) ?>
</title>
If this would be the body of a function, the function definition would be:
function outputHeader(string $title): void {
# ...
}
As we don't have a function by requiring the template files, this is just exemplary. However we could create a generic function that handles requiring a template file and passing the variables to the template (compare include_helper()). In that layer you can also do some ground level error handling (try {} catch (Throwable $throwable) {} etc.). For starters you could collect and group such code in the View class.
What you also likely want to prevent is to bind the view within the controllers' constructor method (Controller::__construct(), ctor in short). It forces you to have a named view - and always the same - makes the controller dependent on that view.
That would mean you couldn't configure any view to any controller. While it wouldn't make sense in most cases to allow an any-to-any relationship here in the concrete practice, it allows you to actually have layer boundaries and to not couple things too tightly (compare: Spaghetti Code 1) and to write code on a higher level (in grade of abstraction, compare Layer of Indirection).
An example in a HTTP application would be to do content negotiation. This would happen on the level of request processing (more in the Router in your example), e.g. a HTTP client requests JSON instead of HTML. Now the HTML templates wouldn't fit here. But the Controller could still do the work if not the view template would be hard-encoded.
To keep things more flexible (so you can use it to a greater extend), one benefit of the MVC model is to use (and to a certain degree somehow pass the result of) the Model by the Controller to the View. It helps you define clear boundaries between those three and keep them more apart from each other (less coupled).
The routing then could negotiate and decide what to bring together, similar as in your example for the Controller already but extended with the View (template), each route could be assigned a layout/template.
As this would work quite the same as with the controller - just for the view - let's see where the current Controller not only is standing in the way for the view but already for the routing (if you find a flaw or bug, look around, often they are not in a single place and alone).
While you already configure the routes in the router, the actual routing you've put in the Controller base-class (Controller::get($route, $controller)). Similar to the __construct() method, this makes the Controller implementation dependent on the Route and even implements the routing. This is pretty convoluted and will certainly become awkward. There is also the problem when you add more routes you loose control which one matches as the matching is done within each Controller etc. . In short, while the code may be functional, it just seems to me it can benefit to be at a different place. As it's about the routing, first place that comes into my mind would be the Router itself. The Router then could do the actual work, "do the routing":
$router = new Router(); # <-- bootstrap
$router->get('/', 'home.contr.php'); # <-- prepare
$router->get('/home', 'home.contr.php'); # <-- prepare
$router->get('/about', 'about.contr.php'); # <-- prepare
$router->get('/portfolio', 'projects.contr.php'); # <-- prepare
$router->route(); # <-- do the work here
The Routers get() method then could stay the same from the outside but you would just store the routes inside and when you invoke the route() method, that configuration is matched against your request implementation.
You could then extend the router configuration with the view name.
It would be then that you still have bound a route to a controller and a view, however you have a central location where this is done (configured/parameterized). Controller and View are more independent to each other and you can concentrate more with their own implementation than the overall wiring which now moved into the router.
Finally while being here, what your example also shows is its dependence on the file-system, you have a certain file-naming convention for the controllers and also the view templates. While it is implicitly necessary to place the code into files, at least in your example on the level of the controllers you can already rely on PHP autoloading. While you want to write everything yourself (e.g. not using a ready-made MVC library), I'd still suggest to make use of some standards, like Autoloader (PSR-4) and as being inherently lazy, make the app a Composer project (it has a composer.json file) as Composer allows you to configure the autoloader and there is a well-defined process developing with it (you can also bring in more easily third-party libraries which you'll certainly need within your application logic, so this is just forward-thinking in a good sense, just start without any requirements just using the Composer autoloader).
So instead of hard-linking controller PHP file-paths, you could say instead that a controller basically is a class definition with at least a single method that the router is able to call. With the autoloader in action, the routing configuration would only need to reference that class/method and PHP then would take care to load the class. This could be done as strings (lazy-loading) or more explicit with the First class callable syntax (PHP 8.1). A good middle-ground for starters perhaps is to have one Controller per class and require to have it an interface so that you have a contract (compare: programming against interfaces 1, 2, 3, 4, 5, 6, 7 etc.). You can then simply pass the class-name and handle the instantiation in the route() method.
$router->get(
/* route */ '/',
/* $controller */ MyApp\MVC\Crontroller\Home::class,
/* $viewName */ 'home'
);
<?php
namespace MyApp\MVC\Controller;
class Home implements Interface {
# ...
}
<?php
namespace MyApp\MVC\Controller;
interface Interface {
public function invoke(InputParameter $params): InvocationResult
}
The route() then could check for the interface to verify some class can be used as a controller (instanceof) and would know how to invoke() the controller by passing the input parameters to receive the result that can be further delegated to the template layer.
This is made possible by also introducing the InputParameter and InvocationResult implementations (classes/interfaces) that help to define the layer boundary of the Controller part.
You can then do something similar for the View layer however the output comes relatively late and you're perhaps not yet settled with it (and you may have different template "engines" depending on use-case) so I would leave it more thin and less engineered and try with the Controllers first and do the delegation in the routing until you learn more about your actual requirements (Session handling, Authentication, Content-Negotiation, Redirects etc.).
At the end of the day you have to make your own decisions here.
Next Steps Suggestions
Add at least one test-script that you can run from your development environment "with a single key-press / click" and simple OK/Fail result (e.g. a simple PHP script that you execute in the shell)
Think about how to improve the error handling so you learn about defects faster (e.g. introduce exception and
Fix the bugs first, your code should actually run first of all (it might not produce the intended results in full but it should at least run - your example does not)
Init Composer / add composer.json to your project
Then change the code to your liking which can benefit having it under test first (compare Unit Tests)

Expand / Override parent's method in constructor filter

I'm working on a WordPress project. There is a child theme implemented, and the parent one has a class to show authors' links. We have implemented a feature to support multiple authors per post, then we have our own methods for showing the proper links (not a single-author link, but many links joined depending on the authors count).
The complete scenario:
Theme: Newspaper
File: td_module.php (includes/wp_booster/td_module.php)
Class: td_module (abstract, many other classes in the parent theme inherit this one)
Method: get_author()
There are many other classes inheriting td_module in the parent theme, hence I cannot just extend td_module in my child theme since every change to the parent theme would be lost when the theme gets updated
Somehow I need to expand this method to show something different, but I don't want to change the method in the td_module class: even when it would be the fastest / most secure solution, this code would be overwritten on every theme upgrade.
This method is widely used in the Theme, that's why is that important to add some code here.
Why I still have some hope: There is a WP filter td_wp_booster_module_constructor, being called on the constructor of td_module class. The problem is that this class doesn't have any other filter on the method for showing the authors, but just the td_wp_booster_module_constructor filter call on the constructor.
The base PHP (and OOP) question: is it possible to expand / replace a method by using this filter in the constructor somehow?
Some code to clarify:
abstract class td_module {
...
function __construct($post, $module_atts = array()) {
...
// Can I change the get_author behavior by using this filter?
apply_filters("td_wp_booster_module_constructor", $this, $post);
// This is the only filter available in the entire class!
...
}
...
function get_author() {
$buffy = '';
// Code for generating author link ($buffy .= ...)
...
// This function doesn't have any apply_filter, there are no filters available
return $buffy;
}
}
Basically, you can't modify a class definition at runtime. The fact that you have a filter available on the constructor is a red herring and really not relevant.
Either the get_author() has some facility to change it's behaviour from outside class definition (in Wordpress parlance, filter and action hooks), or you simply cannot do it.
Outside of Wordpress, in an application with a proper dependency inversion container, you should probably do this by decorating the class and having all class consumer use the decorated class.
But since you have no way of telling class consumers to use one or the other, and a lot of code out of your control is presumably instantiating the class directly, something like this simply won't fly.
There is one extension, Runkit, that allows for changes of behaviour and definitions at runtime. there is even a method to modify a method definition. although I have not tried the extension, and do not know if it runs in an updated PHP runtime.
Note it is almost certainly a very bad idea to do this on production code, and that since you are dealing with code out of your control anyway you can't even be sure you would be changing the definition before it's used for the first time.
I've also found a repo for Runkit that says that it almost works on PHP 7, if you are absolutely convinced on going that way.
Your issue is that the base class is vendor code which you did not write.
So create a class in between your classes extending it!
Job done!
<?php
class SomeWordpressCrap
{
public function doSomething()
{
return 'something';
}
}
class YourAwesomeNewClass extends SomeWordpressCrap
{
public function doSomething()
{
return 'something better!';
}
}
class OneOfYourExistingClasses extends YourAwesomeNewClass
{
}
UPDATE So it turns out the OneOfYourExistingClasses is also vendor code, so the above solution will not work.
However! You could use Roave's "Better Reflection" lib, which you can find here https://github.com/Roave/BetterReflection
This will allow you to "Change the body of a function or method to do something different", which I believe is exactly what you need. Good luck!

How to access libraries inside a controller?

I'm building a small framework that I can use for repeated mundane stuff on future small projects.
I'm stuck on the best way to access libraries from inside a controller. I originally implemented a system similar to CodeIgniter's whereby my main controller class is basically a super object and loads all the classes into class variables which are then accessed by extending the controller and doing like $this->class->method()
I find that a little ugly, though. So I thought of just loading each class individually on a per-use basis in each controller method.
What's the best (cleanest) way of doing this?
To only ever have one instance of each class, you could create a simple service container.
class ServiceContainer
{
protected $services;
public function get($className)
{
if (!array_key_exists($className, $this->services)) {
$this->services[$className] = new $className;
}
return $this->services[$className]
}
}
Then create one ServiceContainer instance per application. Inject the container into all of your controllers and use
public function someAction()
{
$this->container->get('Mailer')->send($email_data);
}
Simple example, and obviously needs a lot of work to make useable (for instance autoloading needed and handling of file paths for ease of use, or easier way to add services without getting them, etc).
I dont like the way CodeIgniter does it. Its never seemed right to me. I favor an auto loading class pushed onto the spl_autoload stack. And then just calling the class as normal like:
$class = new SomeClass();
PHP provides autoload functionality with SPL and spl_autoload (and related functions). You can register a custom autoloader for your library code.
For the shared functionality handled by your application, have you considered the Front Controller design pattern?

PHP workaround to extend classes of the same name?

I know extending a class with the same name is not possible, but I was curious if anyone knew of a way to load a class then rename it, so i can later extend it with the original name. Hopefully like something below:
<?php
//function to load and rename Class1 to Class2: does something like this exist?
load_and_rename_class('Class1', 'Class2');
//now i can extend the renamed class and use the original name:
class Class1 extends Class2{
}
?>
EDIT:
Well, I understand that this would be terrible practice in a basic OOP environment where there are large libraries of class files. But i'm using the CakePHP MVC framework and it would make great sense to be able to extend plugin classes in this way since the framework follows a well established naming convention (Model names, view names, controller names, url routes (http://site.com/users), etc).
As of now, to extend a CakePHP plugin (eg: Users plugin) you have to extend all the model, view, and controller classes each with different names by adding a prefix (like AppUsers) then do some more coding to rename the variable names, then you have to code the renamed url routes, etc. etc. to ultimately get back to a 'Users' name convention.
Since the MVC framework code is well organized it would easily make sense in the code if something like the above is able to be implemented.
I'm trying to work out why this would be necessary. I can only think of the following example:
In a context that you have no control over, an object is initialised:
// A class you can't change
class ImmutableClass {
private function __construct() {
$this->myObject = new AnotherImmutableClass();
}
}
$immutable = new ImmutableClass();
// And now you want to call a custom, currently non existing method on myObject
// Because for some reason you need the context that this instance provides
$immutable->myObject->yourCustomMethod();
And so now you want to add methods to AnotherImmutableClass without editing either Immutable class.
This is absolutely impossible.
All you can do from that context is to wrap that object in a decorator, or run a helper function, passing the object.
// Helper function
doSomethingToMyObject($immutable->myObject);
// Or decorator method
$myDecoratedObject = new objectDecorator($immutable->myObject);
$myDecoratedObject->doSomethingToMyObject();
Sorry if I got the wrong end of the stick.
For more information on decorators see this question:
how to implement a decorator in PHP?.
I happen to understand why you would want to do this, and have come up with a way to accomplish what the end goal is. For everyone else, this is an example of what the author may be dealing with...
Through out a CakePHP application you may have references to helper classes (as an example > $this->Form->input();)
Then at some point you may want to add something to that input() function, but still use the Form class name, because it is through out your application. At the same time though you don't want to rewrite the entire Form class, and instead just update small pieces of it. So given that requirement, the way to accomplish it is this...
You do have to copy the existing class out of the Cake core, but you do NOT make any changes to it, and then when ever you upgrade cake you simply make an exact copy to this new directory. (For example copy lib/Cake/View/Helper/FormHelper.php to app/View/Helper/CakeFormHelper.php)
You can then add a new file called app/View/Helper/FormHelper.php and have that FormHelper extend CakeFormHelper, ie.
App::uses('CakeFormHelper', 'View/Helper');
FormHelper extends CakeFormHelper {
// over write the individual pieces of the class here
}

Best practices for managing several specialized versions of one app

I have a web application that has many faces and so far I've implemented this through creating themes. A theme is a set of html, css and images to be used with the common back end.
Things are laid out like so:
code/
themes/theme1
themes/theme2
And each instance of the web application has a configuration file that states which theme should be used. Example:
theme="theme1"
Now new business rules are asking me to make changes to certain themes that can't be achieved through simply change the html/css/images and require changing the backend. In some cases these changes need to be applied to a group of themes.
I'm wondering how to best lay this out on disk, and also how to handle it in code. I'm sure someone else must have come up against this.
One idea is to have:
code/common
code/theme1
code/theme2
themes/theme1
themes/theme2
Then have my common code set the include_path such that code/theme1 is searched first, then code/common.
Then if I want to specialize say the LogoutPage class for theme2, I can simply copy the page from code/common to the same path under code/theme2 and it will pick up the specialized version.
One problem with this idea is that there'll be multiple classes with the same name. Although in theory they would never be included in the same execution, I wouldn't be able to extend the original base class.
So what if I was to make a unique name for the base class? e.g. Theme1LogoutPage extends LogoutPage. One problem I can foresee with that is when some common code (say the Dispatcher) references LogoutPage. I can add conditions to the dispatcher, but I wonder if there's a more transparent way to handle this?
Another option I can think of is to maintain separate branches for each theme, but I think this could be a lot of work.
One final thing to consider is that features might originate in one theme and then require merging into the common codebase.
Any input greatly appreciated. If it makes any difference, it's a LAMP environment.
I don't have a specific recommendation. However, I strongly suggest to NOT take shortcut... Use the solution that will you will find comfortable to add a third theme or to change something next year.
Duplication is the enemy of maintainability.
I'd investigate using the Strategy pattern as a means to implement different functionality in different versions of the site. Have a Factory that takes in your configuration and supplies the appropriate code strategy based on it. Each strategy can implement some common interface so that they are interchangeable from the calling class' point of view. This will isolate your changes to implement new strategies to the Factory class, Configuration class, and any new strategy classes that you need to implement to make the change. You could do the same (or similar) with any user controls that need to differ between the different versions.
I'll illustrate with pseudocode (that may look suspiciously like C#)
public interface ILogoutStrategy
{
void Logout();
}
public abstract class AbstractLogoutStrategy : ILogoutStrategy
{
public virtual void Logout()
{
// kill the sesssion
}
}
public class SingleSiteLogoutStrategy : AbstractLogoutStrategy
{
public void Logout()
{
base.Logout();
// redirect somewhere
}
}
public class CentralAuthenticationSystemLogoutStrategy : AbstractLogoutStrategy
{
public void Logout()
{
base.Logout();
// send a logout request to the CAS
// redirect somewhere
}
}
public static class StrategyFactory
{
public ILogoutStrategy GetLogoutStrategy(Configuration config)
{
switch (config.Mode)
{
case Mode.CAS:
return new CentralAuthenticationSystemLogoutStrategy();
break;
default:
case Mode.SingleSite:
return new SingleSiteLogoutStrategy();
break;
}
}
}
Example usage:
ILogoutStrategy logoutStrategy = StrategyFactory.GetLogoutStrategy( config );
logoutStrategy.Logout();
Are you using Master Pages? If you need different layout and UI stuff you could just have a different set of master pages for each of your instances. If you need custom behavior then you might want to look into Dependency Injection. Spring.NET, etc.
What you need are templates.
Thence you can separate your code from your presentation.
I highly recommend smarty templates. Also PEAR template_it.
http://www.smarty.net/
This also make your code far more maintainable. The aim is to have no html in your php, and to have no php in your html.
then all you will need to do is change the html template that is being used for each theme. or folder of templates.
You could have:
/common/code
And:
/sitename/code
All files in /common/code are abstract classes.
For every file in /common/code, just create a corresponding non-abstract class file in /sitename/code that INHERITS from the abstract class in /common/code.
This way you only need to implement CHANGES in the /sitename/code
Everything else is core functionality that exists only in /common/code
The important thing to do here is ensure that you only add public methods to the abstract classes. This way the methods are available to all sites, and classes from all sites can be treated/worked with identically.
I would do:
[theme name]/[subfolder]
default/common
default/common/html
default/common/css
red/code
red/common
red/common/html
red/common/css
red/code
green/common
green/common/html
So if the code or any other component doesn't exist it will fall back to default.
But in fact I would branch the website in svn, so common code if it evolves I can merge it, etc.. see Subversion at: http://subversion.tigris.org/

Categories