Override third party class method inside CakePHP model - php

I am starting a new project which involves scraping websites and so am planning on using PHPCrawl http://cuab.de/quickstart.html as it looks like the best PHP based solution for this (unless anybody has any other suggestions) but have run into a problem that I can't quite get my head around.
So I import the PHPCrawl class with
App::import('Vendor', 'PHPCrawl', array('file' => 'PHPCrawl/libs/PHPCrawler.class.php'));
Then just underneath that I extend the PHPCrawl class to handle the data like so
class MyCrawler extends PHPCrawler{
function handleDocumentInfo($DocInfo) {
//handle data here
}
}
But my problem comes in when I need to perform CakePHP methods like create() and save() from within that method. I tried creating a new instance of the CakePHP model within the class but that gives me a warning:
Maximum function nesting level of '100' reached, aborting!
So I assume that this is creating an infinite loop of class instances. I guess what I want to do is override the handleDocumentInfo() function but within my CakePHP class, is that possible?
Apologies if this isn't clear, I don't quite know how to go about this one!

For anybody having a similar problem. I got around this by creating a component, importing the class and overriding the method inside that. Had to mess with the original class a little and add a new global var but all seems to be working

Related

Load model into $this scope

I'm doing a learning exercise where I'm building a pretty basic MVC framework. I'm really only doing this to learn more about OOP and it's pros, cons and common pitfalls.
I'm trying to replicate a behaviour, or syntax might be more correct, similar to the very popular framework codeigniter.
That is, I want to manually be able to load a Model from inside my Controller.
Here's how I want to perform it, and subsequently use it.
$this->load->model("mymodel");
$this->mymodel->some_function();
I have my loader working, it tries to run the class and this is how the load->model code looks like
public function model($model)
{
if(file_exists(APPLICATION.'models/'.$model.'.php'))
{
include(APPLICATION.'models/'.$model.'.php');
$this->{$model} = new $model;
}
}
The problem I am having is I get a error running this code saying that the class $model (this should be transformed into mymodel in this case) doesn't exist.
How do I make it so that $model translates into mymodel so the code would perform a action as such: new mymodel;
Thanks for any help, I'm quite the novice in OOP so I might have gotten confused here but really cannot seem to figure this out :/
$this->{$model} does, however, translate into $this->mymodel.
Instead of creating a new thread, I'll add to this.
My next issue has already arisen, since it's basically a followup problem I'll add it here too.
$this->mymodel->some_function() returns the following error;
Notice: Undefined property: Home::$mymodel in C:\xampp\htdocs\application\controllers\home.php on line 16
This error shows when running $this->mymodel->some_function();
Home is the loaded controller.
Hultin

Open / Closed principle - How to call the new versions?

I'm trying to grasp the Open/Closed principle (in my case for PHP, but that shouln't really make a difference).
The way i understand it is that a class is never open for modification. Only for bug fixing. If i wanted to add new code to the class, then i'd have to create a new one and extend the 'old' class. That's the only way i can add new code to it.
In a way i can see the advantages of this. Because basically you create some sort of versioning system, where old code always work, but you can always try to use the new classes too.
But how does this work in practice? I mean, suppose i have the following class:
class MyObject
{
public function doSomething()
{
echo 'Im doing something';
}
}
So somewhere i'm probably instantiating this class:
$obj = new MyObject();
But then i decide that it's good to have another method in that object. So i can do something else too. According to the OCP i can't modify the class. So i have to create a new one, which extends to old one right?
First problem. How do i call the new class? Because it isn't really a complete new object. Like. a User object is a User object. I can't suddenly give it completely diffent name just because it needs another method. Anyway, i create the new class:
class MyNewObject extends MyObject
{
public function doSomethingElse()
{
echo 'Im doing something else now';
}
}
Now this also means i have to change the line of code where i instantiated the "MyObject" class and replace it with the "MyNewObject" class, right..? And if that's done in more than one place, then i have to search through my source code... (Think about a method in a controller class, which almost always uses the 'new' keyword to instantiate certain classes).
The same basically applies to inheritance. I'd have to find each class the inherits the old class and have to replace that with the new class.
So basically my questions are:
How do you name the new classes which have the new methods? Just becasue i added some new functionality, doesn't mean i can give the class a whole new name...
And what if the 'old' classs is instantiated (or inherited) from multiple places. Then i'd have to find all of those places... Where's the gain?
The Open Closed Principle isn't intended to be used as a kind of version control system. If you really need to make changes to the class, then go ahead and make those changes. You don't need to create new classes and change all the places that instantiated the old one.
The point of the Open Closed Principle is that a well-designed system shouldn't require you to change existing functionality in order to add new functionality. If you are adding a new class to the system, you shouldn't need to search through all your code to find the places where you need to reference that class or have special cases for it.
If the design of your class isn't flexible enough to handle some new piece of functionality, then by all means change the code in your class. But when you change the code, make it flexible so you can handle similar changes in the future without code changes. It's meant to be a design policy not a set of handcuffs to prevent you from making changes. With good design decisions, over time your existing code will require fewer and fewer changes when you add new functionality to the system. It's an iterative process.
I would argue that by adding a function, you're not modifying the class behavior.
In all the instances where doSomething() is currently being called in your app, simply by adding doSomethingElse() to the class will have no effect. Since you're not changing doSomething(), the behavior is the same as it was before.
Once you determine that your doSomething() implementation isn't cutting it for certain circumstances, you can extend the class and override doSometing(). Again, the original still behaves the same as it always did, but now you have a new doSomething() to work with also.
I realize that this goes against the strict definition of open/closed, but this is the real world, and that's how I interpreted that principle in my code.
You need to create a constructor in your MyNewObject class which calls the parent class' constructor:
function __construct() {
parent::__construct();
}
This way you can instantiate the new class and still access all the functionality of the extended one.
You can then also override any function in the parent class (as long as it is not marked final of course).
So you could do:
$newObj = new MyNewObject();
$newObj->doSomething();
$newObj->doSomethingElse();

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
}

Calling overridden method from within overriding method in OO PHP

Working in a symfony model, I want to override a function and call the overridden function from within the overriding one, along the lines of
class MyClass extends BaseMyClass {
function setMyProperty($p) {
parent::setMyProperty($p);
//do some other stuff
}
}
This is resulting in a segmentation fault. I don't want to alter the parent class - it's been generated by symfony, and may feasibly be overwritten in the future if the model is rebuilt. This seems like something that should be straightforward, but I'm struggling to find the solution.
Since you saw the problem, I guess you should mark it as answered to remove it from the unanswered list.
I've managed to find a solution to my own question on the symfony project forum.
I can't call the overridden function because it doesn't exist. Though it exists enough for me to override it.
Using
$this->_set('my_property', $p);
Works where
parent::setMyProperty($p);
Causes the error.
Note that
$this->setMyProperty($p);
Works fine in my class if the method has not been overridden.

Categories