I've got an Object Oriented library I wanted to add a method to, and while I'm fairly certain I could just go into the source of that library and add it, I imagine this is what's generally known as A Bad Idea.
How would I go about adding my own method to a PHP object correctly?
UPDATE ** editing **
The library I'm trying to add a method to is simpleHTML, nothing fancy, just a method to improve readability. So I tried adding to my code:
class simpleHTMLDOM extends simple_html_dom {
public function remove_node() {
$this->outertext = "";
}
}
which got me: Fatal error: Call to undefined method simple_html_dom_node::remove_node(). So obviously, when you grab an element in simpleHTML it returns an object of type simple_html_dom_node.
If I add the method to simple_html_dom_node my subclass isn't what will be created by simplHTML ... so stuck as to where to go next.
A solution would be to create a new class, that extends the one from your library -- and, then, use your class, which have have all methods of the original one, plus yours.
Here's a (very quick and simple) example :
class YourClass extends TheLibraryClass {
public function yourNewMethod() {
// do what you want here
}
}
And, then, you use your class :
$obj = new YourClass();
$obj->yourNewMethod();
And you can call the methods of the TheLibraryClass class, as yours inherits the properties and methods of that one :
$obj->aMethodFromTheLibrary();
About that, you can take a look at the Object Inheritance section of the manual.
And, as you guessed, modifying a library is definitly a bad idea : you'll have to re-do that modification each time you update the library !
(One day or another, you'll forget -- or one of your colleagues will forget ^^ )
You could do it with inheritance, but you could also use a decorator pattern if you do not need access to any protected members from SimpleHtml. This is a somewhat more flexible approach. See the linked page for details.
class MySimpleHtmlExtension
{
protected $_dom;
public function __construct(simple_html_dom $simpleHtml)
{
$this->_dom = $simpleHtml;
}
public function removeNode(simple_html_dom_node $node)
{
$node->outertext = '';
return $this;
}
public function __call($method, $args)
{
if(method_exists($this->_dom, $method)) {
return call_user_func_array(array($this->_dom , $method), $args));
}
throw new BadMethodCallException("$method does not exist");
}
}
You'd use the above like this
$ext = new MySimpleHtmlExtension( new simple_html_dom );
$ext->load('<html><body>Hello <span>World</span>!</body></html>');
$ext->removeNode( $ext->find('span', 0) );
I don't why adding the method would be bad, however if you want to so without editing the library, your best bet would be to extend the class like so:
class NewClass extends OldClass {
function newMethod() {
//do stuff
}
}
class myExtenstionClass extends SomeClassInLibrary
{
public function myMethod()
{
// your function definition
}
}
As Pascal suggests... read the manual :-)
Related
I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.
I'm looking for more comfortable/more short version of Switch() statement in case of using multiple functions.
I'll give you one example: imagine 100-200 functions in one class, and you want to call only one of them by setting value to id in that class.
In my particular case, I have the following structure of PHP file:
<?php
class _main
{
function request($id)
{
switch($id)
{
case 0:
$this->writeA();
break;
case 1:
$this->writeB();
break;
///...
// then we have 100-200 functions like this in switch.
}
}
function writeA()
{
echo('a');
}
function writeB()
{
echo('b');
}
}
$id = 1;
$x = new _main();
$x->request($id);
?>
For some of you it may seem weird, but I don't want to have that much lines of code with case and break. For me, they are just making code more difficult to read.
(by the way, writing it 100 times will not making it fun for me too).
CONCLUSION
What could be the best,fast and comfortable method?
Can I store functions to array and then call them?
And will it affect performance? Will be Swicth() even faster?
Thank you :)
EDIT
Perhaps there is a different way of thinking/coding and not only array/switch thing.
I can't say I would ever recommend this but if you really want that many methods within a single class and a singular function to route the calls through...
<?php
class MyClass
{
public $id;
public function callFunction()
{
$funcName = 'execute' . $this->id;
return $this->$funcName();
}
private function execute1()
{
echo 'execute1() Called.';
}
private function execute2()
{
echo 'execute2() Called.';
}
}
$c = new MyClass();
$c->id = 1;
$c->callFunction();
Output:
execute1() Called.
I feel like there is most likely another way to approach this with more information utilising Interfaces and Abstract classes, but with the information to go off the above might suffice your requirement.
Edit: Sadly I don't have the time right now to come up with a detailed solution, and I don't really have enough information to go off but perhaps utilising interfaces is your best solution for your requirement. Below is a very quick example.
<?php
interface WritableInterface
{
public function write($data);
}
class VersionOneWriter implements WritableInterface
{
public function write($data)
{
return $data . '<br/>';
}
}
class VersionTwoWriter implements WritableInterface
{
public function write($data)
{
return $data . $data . '<br/>';
}
}
class MyMainClass
{
public function request(WritableInterface $writer, $data)
{
return $writer->write($data);
}
}
$c = new MyMainClass();
$w1 = new VersionOneWriter();
$w2 = new VersionTwoWriter();
echo $c->request($w1, 'DataString');
echo $c->request($w2, 'DataString');
Essentially when you call your request function you pass along a Writer class which implements the WritableInterface. Anything that implements that interface has to have a write() method.
Now when you pass your data across with your method, since you are also passing a writer along that can handle the data you can safely call ->write($data) within your request() method and the result will be dependent on the class you passed through.
If you ever need another method of writing you can just add create another class that implements your interface
Hopefully that made some sense, it was a bit of a ramble as I have to disappear for a bit. If you have any questions I'll try to check back when I have time.
--
Edit2:
The define() in this instance requires PHP7+ since I'm defining an array, but you could prior to PHP7 you could just use a standard array. $classMap = ['FirstClass', 'SecondClass'];
interface MyInterface {}
class FirstClass implements MyInterface {}
class SecondClass implements MyInterface {}
$requestParam = 1;
define('CLASS_MAP', array(
'FirstClass',
'SecondClass',
));
$classMap = CLASS_MAP[$requestParam]; // SecondClass
$class = new $classMap;
var_dump($class); // Dumps out: object(SecondClass)#1 (0) {}
I'm creating a form generator and i'm actually working on an specific element : DuplicableElement.
The form can contains a special container (DuplicableContainer) that used jQuery plugin (sheepit) to duplicate itself.
To get working this, the DuplicatableContainer can only have DuplicableElement in its child because they are a lot of things specific to them.
The problem is that php does not allow multiple inheritance. (should works with php "Traits" but not supported actually). I can't make a DuplicableTextBox extends simultaneously DuplicableFormElement and the normal TextBox element.
I hope i was enough clear.
Edit: Even with "Traits", the problem still exists, and i can't find a clean solution.
You can also inject behaviors/plugins into your elements providing the functionality of traits but without official support.
<?php
class Car {
public $behaviors = array();
public function __call($method, $params) {
foreach($this->behaviors as $behavior) {
if (method_exists($behavior, $method)) {
return call_user_func_array(array($behavior, $method), $params);
}
}
}
}
class Engine {
public function start() { echo 'Started'; }
}
$car = new Car;
$engine = new Engine;
$car->behaviors[] = $engine;
$car->start(); // Started
Do the DuplicableElements specifically need to be a class? If the public interface to the object is what matters, you could make DuplicableElement an interface instead. A class can extend one class and implement multiple interfaces all at once.
Here's the objective. I have a PHP class and there are one or two of its methods that I would like to override with my own. As I understand OOP (in PHP and in general) I could write a child class that extends it and overrides the functionality of the methods in question.
However, I was wondering if this is the best way of achieving this task and if this is a proper use for child classes or if there is something better in PHP for what I'm trying to do.
Yes, that is the best way to do it. The idea of extending a class is to provide extra or more specific functionality.
yes. this exactly what you use inheritance for.
A good principle though, is make sure your new inheriting class "is-a" base class.
Like Human is-a Mammal. Don't do Alien extends Human just because Alien does a lot of stuff Humans do.
So, are you asking if overriding methods (which requires extending the superclass) is the best way to override methods? Yes.
That is the most important use of subclasses. In fact, polymorphism is at the heart of OOP. The other use is to provide new properties/methods, but that usually won't be enough.
class oldClass {
public function methodToOverride() {
echo 'oh hai';
}
}
class childClass extends oldClass {
public function methodToOverride($arg, $arg2) {
// your custom code
echo 'hai world ' . $arg . ' ' . $arg2;
// if you need to still call the parent class
parent::methodToOverride();
}
}
$child = new childClass();
$child->methodToOverride('nom', 'nom');
Sounds like you're on the right track.
For the methods you want to override, you'd probably want to:
make sure you're not changing the intent of the method
know whether you have to call the base class's method within, and when
The only other way, would be to create a generic "superclass"...
class SuperClass {
protected $obj = null;
protected $overrides = array();
public function __construct($obj) {
if (!is_object($obj)) {
throw new InvalidArgumentException('Argument is not an object');
}
$this->obj = $obj;
}
public function __call($method, $args) {
$method = strtolower($method);
if (isset($this->overrides[$method])) {
array_unshift($args, $this);
return call_user_func_array($this->overrides[$method], $args);
} elseif (is_callable(array($this->obj, $method))) {
return call_user_func_array(array($this->obj, $method), $args);
} else {
throw new BadMethodCallException('Invalid Method Called');
}
}
public function __get($var) {
return isset($this->obj->$var) ? $this->obj->$var : null;
}
public function __set($var, $value) {
$this->obj->$var = $value;
}
public function addOverride($method, $callback) {
$this->overrides[strtolower($method)] = $callback;
}
}
It's not always the best solution, but it's possible that some situations exist to use something like that. It will let you "add" and "override" methods to any object at run time.
The better generic solution is to simply extend the class in a child class... But the above "superclass" does have some uses...
Another route I haven't seen mentioned here is to question whether it wouldn't be better to extract an abstract class and have the two classes extend that. Of course you would need to be able to alter the code of the first class for that (e.i. if the class came from an open source library you might refrain from changing the code).
you are on the right track
Is there any way to redefine a class or some of its methods without using typical inheritance? For example:
class third_party_library {
function buggy_function() {
return 'bad result';
}
function other_functions(){
return 'blah';
}
}
What can I do to replace buggy_function()? Obviously this is what I would like to do
class third_party_library redefines third_party_library{
function buggy_function() {
return 'good result';
}
function other_functions(){
return 'blah';
}
}
This is my exact dilemma: I updated a third party library that breaks my code. I don't want to modify the library directly, as future updates could break the code again. I'm looking for a seamless way to replace the class method.
I've found this library that says it can do it, but I'm wary as it's 4 years old.
EDIT:
I should have clarified that I cannot rename the class from third_party_library to magical_third_party_library or anything else because of framework limitations.
For my purposes, would it be possible to just add a function to the class? I think you can do this in C# with something called a "partial class."
It's called monkey patching. But, PHP doesn't have native support for it.
Though, as others have also pointed out, the runkit library is available for adding support to the language and is the successor to classkit. And, though it seemed to have been abandoned by its creator (having stated that it wasn't compatible with PHP 5.2 and later), the project does now appear to have a new home and maintainer.
I still can't say I'm a fan of its approach. Making modifications by evaluating strings of code has always seemed to me to be potentially hazardous and difficult to debug.
Still, runkit_method_redefine appears to be what you're looking for, and an example of its use can be found in /tests/runkit_method_redefine.phpt in the repository:
runkit_method_redefine('third_party_library', 'buggy_function', '',
'return \'good result\''
);
runkit seems like a good solution but its not enabled by default and parts of it are still experimental. So I hacked together a small class which replaces function definitions in a class file. Example usage:
class Patch {
private $_code;
public function __construct($include_file = null) {
if ( $include_file ) {
$this->includeCode($include_file);
}
}
public function setCode($code) {
$this->_code = $code;
}
public function includeCode($path) {
$fp = fopen($path,'r');
$contents = fread($fp, filesize($path));
$contents = str_replace('<?php','',$contents);
$contents = str_replace('?>','',$contents);
fclose($fp);
$this->setCode($contents);
}
function redefineFunction($new_function) {
preg_match('/function (.+)\(/', $new_function, $aryMatches);
$func_name = trim($aryMatches[1]);
if ( preg_match('/((private|protected|public) function '.$func_name.'[\w\W\n]+?)(private|protected|public)/s', $this->_code, $aryMatches) ) {
$search_code = $aryMatches[1];
$new_code = str_replace($search_code, $new_function."\n\n", $this->_code);
$this->setCode($new_code);
return true;
} else {
return false;
}
}
function getCode() {
return $this->_code;
}
}
Then include the class to be modified and redefine its methods:
$objPatch = new Patch('path_to_class_file.php');
$objPatch->redefineFunction("
protected function foo(\$arg1, \$arg2)
{
return \$arg1+\$arg2;
}");
Then eval the new code:
eval($objPatch->getCode());
A little crude but it works!
For people that are still looking for this answer.
You should use extends in combination with namespaces.
like this:
namespace MyCustomName;
class third_party_library extends \third_party_library {
function buggy_function() {
return 'good result';
}
function other_functions(){
return 'blah';
}
}
Then to use it do like this:
use MyCustomName\third_party_library;
$test = new third_party_library();
$test->buggy_function();
//or static.
third_party_library::other_functions();
For the sake of completeness - monkey patching is available in PHP through runkit. For details, see runkit_method_redefine().
How about wrapping it in another class like
class Wrapper {
private $third_party_library;
function __construct() { $this->third_party_library = new Third_party_library(); }
function __call($method, $args) {
return call_user_func_array(array($this->third_party_library, $method), $args);
}
}
Yes, it's called extend:
<?php
class sd_third_party_library extends third_party_library
{
function buggy_function() {
return 'good result';
}
function other_functions(){
return 'blah';
}
}
I prefixed with "sd". ;-)
Keep in mind that when you extend a class to override methods, the method's signature has to match the original. So for example if the original said buggy_function($foo, $bar), it has to match the parameters in the class extending it.
PHP is pretty verbose about it.
Zend Studio and PDT (eclipse based ide) have some built in refractoring tools. But there are no built in methods to do this.
Also you wouldn't want to have bad code in your system at all. Since it could be called upon by mistake.
I've modified the code from the answer by #JPhilly and made it possible to rename a the patched class to avoid errors.
Also, I've changed the regex that identifies the about-to-be-replaced function to fit cases where the replaced function doesn't have any class access modifiers in front of its name
Hope it helps.
class Patch {
private $_code;
public function __construct($include_file = null) {
if ( $include_file ) {
$this->includeCode($include_file);
}
}
public function setCode($code) {
$this->_code = $code;
}
public function includeCode($path) {
$fp = fopen($path,'r');
$contents = fread($fp, filesize($path));
$contents = str_replace('<?php','',$contents);
$contents = str_replace('?>','',$contents);
fclose($fp);
$this->setCode($contents);
}
function redefineFunction($new_function) {
preg_match('/function ([^\(]*)\(/', $new_function, $aryMatches);
$func_name = trim($aryMatches[1]);
// capture the function with its body and replace it with the new function
if ( preg_match('/((private|protected|public)?\s?function ' . $func_name .'[\w\W\n]+?)(private|protected|public|function|class)/s', $this->_code, $aryMatches) ) {
$search_code = $aryMatches[1];
$new_code = str_replace($search_code, $new_function."\n\n", $this->_code);
$this->setCode($new_code);
return true;
} else {
return false;
}
}
function renameClass($old_name, $new_name) {
$new_code = str_replace("class $old_name ", "class $new_name ", $this->_code);
$this->setCode($new_code);
}
function getCode() {
return $this->_code;
}
}
This is how I've used it to patch a Wordpress plugin:
$objPatch = new Patch(ABSPATH . 'wp-content/plugins/a-plugin/code.php');
$objPatch->renameClass("Patched_AClass", "Patched_Patched_AClass"); // just to avoid class redefinition
$objPatch->redefineFunction("
function default_initialize() {
echo 'my patched function';
}");
eval($objPatch->getCode());
$result = new Patched_AClass();
If the library is explicitly creating the bad class and not using a locater or dependency system you are out of luck. There is no way to override a method on another class unless you subclass.
The solution might be to create a patch file that fixes the library, so you can upgrade the library and re-apply the patch to fix that specific method.
You might be able to do this with runkit. http://php.net/runkit
You can make a copy of the library class, with everything the same except the class name. Then override that renamed class.
It's not perfect, but it does improve the visibility of the extending class's changes. If you fetch the library with something like Composer, you'll have to commit the copy to source control and update it when you update the library.
In my case it was an old version of https://github.com/bshaffer/oauth2-server-php. I modified the library's autoloader to fetch my class file instead. My class file took on the original name and extended a copied version of one of the files.
Since you always have access to the base code in PHP, redefine the main class functions you want to override as follows, this should leave your interfaces intact:
class third_party_library {
public static $buggy_function;
public static $ranOnce=false;
public function __construct(){
if(!self::$ranOnce){
self::$buggy_function = function(){ return 'bad result'; };
self::$ranOnce=true;
}
.
.
.
}
function buggy_function() {
return self::$buggy_function();
}
}
You may for some reason use a private variable but then you will only be able to access the function by extending the class or logic inside the class. Similarly it's possible you'd want to have different objects of the same class have different functions. If so, do't use static, but usually you want it to be static so you don't duplicate the memory use for each object made. The 'ranOnce' code just makes sure you only need to initialize it once for the class, not for every $myObject = new third_party_library()
Now, later on in your code or another class - whenever the logic hits a point where you need to override the function - simply do as follows:
$backup['buggy_function'] = third_party_library::$buggy_function;
third_party_library::$buggy_function = function(){
//do stuff
return $great_calculation;
}
.
.
. //do other stuff that needs the override
. //when finished, restore the original function
.
third_party_library::$buggy_function=$backup['buggy_function'];
As a side note, if you do all your class functions this way and use a string-based key/value store like public static $functions['function_name'] = function(...){...}; this can be useful for reflection. Not as much in PHP as other languages though because you can already grab the class and function names, but you can save some processing and future users of your class can use overrides in PHP. It is however, one extra level of indirection, so I would avoid using it on primitive classes wherever possible.
There's alway extending the class with a new, proper, method and calling that class instead of the buggy one.
class my_better_class Extends some_buggy_class {
function non_buggy_function() {
return 'good result';
}
}
(Sorry for the crappy formatting)