I have a helper called Zend_View_Helper_FormVars that's used by one of my modules.
I also have a common helper in application/common/helpers/GeneralFunctions.php
I'm trying to call a function from Zend_View_Helper_FormVars that's in GeneralFunctions.php.
Here is the short version of Zend_View_Helper_FormVars:
class Zend_View_Helper_FormVars
{
public $reqFieldVisual='<span class="req">*</span>';
public $roles=array('admin'=>'admin', 'user'=>'user');
public $paymentMethods=array('1'=>'Check', '2'=>'Credit Card',
'3'=>'Cash', '4'=>'Other');
public function formVars(){
$this->baseUrl=Zend_Controller_Front::getInstance()->getBaseUrl();
return $this;
}
public function mkCategoryCodeSelectGroup($codeTypeArr=array(),
$codesArr=array()) {
$html='';
$html.=Zend_View_Helper_GeneralFunctions::generalFunctions()->progressMeter();
return $html;
}
}
Here is the code in GeneralFunctions.php:
class Zend_View_Helper_GeneralFunctions
{
public function generalFunctions(){
$this->baseUrl=Zend_Controller_Front::getInstance()->getBaseUrl();
return $this;
}
public function progressMeter() {
$html='';
$html.='<span id="progressWrapper">';
$html.='<span id="progressMeter"></span>';
$html.='</span>';
$html.='';
return $html;
}
}
Also, forgot to mention that I have the GeneralFunctions helper auto loaded in the Bootstrap like this and it's available to all my modules already:
$view->addHelperPath(APPLICATION_PATH .'/common/helpers', 'View_Helper');
Here is what I tried, but am getting an error:
// application/Bootstrap.php ----------->
function _initViewHelpers() {
// add a helper for use for all modules
$view->addHelperPath(APPLICATION_PATH .'/Common/Helper', 'Common_Helper');
}
//-------------------->
// application/common/helpers/General.php ----------->
class Zend_View_Helper_General extends Zend_View_Helper_Abstract
{
public function general(){
return $this;
}
public function test(){
return 'test 123';
}
}
//-------------------->
// application/modules/dashboard/views/helpers/DashboardHelper.php ----------->
class Zend_View_Helper_DashboardHelper extends Common_Helper_General
{
public function dashboardHelper(){
return $this;
}
public function dashboardTest(){
return 'from dashboard';
}
}
//-------------------->
// application/modules/dashboard/views/scripts/index/index.phtml ----------->
echo $this->dashboardHelper()->test();
//-------------------->
Error message I get:
Fatal error: Class 'Common_Helper_General' not found in /Applications/MAMP/htdocs/mysite/application/modules/dashboard/views/helpers/DashboardHelper.php on line 2
It's actually really simple to call another View Helper.
Make sure that your view helper extends Zend_View_Helper_Abstract, so that it has access to the $view. Then you may simply call helpers as you would from a view, i.e.
$this->view->generalFunctions()->progressMeter();
Based on your example above:
<?php
class Zend_View_Helper_FormVars extends Zend_View_Helper_Abstract {
/* ... */
public function mkCategoryCodeSelectGroup($codeTypeArr=array(),
$codesArr=array()) {
$html='';
$html. $this->view->generalFunctions()->progressMeter();
return $html;
}
}
You possibly haven't configured your autoloader to load classes from the application/common/helpers/ folder.
See Zend_Application_Module_Autoloader for default paths. You should add your new folder to this.
I see several problems with your provided code.
You are attempting to call Zend_View_Helper_GeneralFunctions::generalFunctions() as a static method when it is declared as a class method (ie you have to instantiate an instance of the class to use it) by reason of your omission of the static keyword.
If you in fact want to use generalFunctions() as a static method and correct this then you will need to either make baseUrl a static property or you will have to instantiate an instance of the class and then return that instance.
The idea of using your GeneralFunctions class as a container for static methods that are called directly is really a symptom of deeper problems and is rightly labeled a code smell. If you think that I'm lying take a look at the high priority items for the Zend Framework 2.0 (hint: it involves removing all static methods from the framework). Or you can always ask SO what they think of static methods :-).
Looking at your given class name for the general functions class Zend_View_Helper_GeneralFunctions and given the current scenario where you are trying to use the GeneralFunctions helper inside another helper, I would surmise that you really need to do one of two things.
You need to have every helper class subclass the GeneralFunctions class so that all of your helpers have these functions available. Basically, ask yourself if your helpers all start out life as GeneralFunction helpers with extended functionality beyond. This solution uses inheritance to solve your problem.
Every view helper should contain an instance of the View object being acted upon. Therefore in theory you should be able to access any other view helper via the magic __call method (I think there is also an explicit method but I always use the magic method). It might look like so in your scenario:
public function mkCategoryCodeSelectGroup($codeTypeArr=array(), $codesArr=array())
{
$html='';
$html.= $this->generalFunctions()->progressMeter();
return $html;
}
In this scenario the __call method would load the GeneralFunctions helper and would then would call the progressMeter() method from the GeneralFunctions helper.
Now your GeneralFunctions helper class would probably look like this:
class Zend_View_Helper_GeneralFunctions
{
public function __construct()
{
$this->baseUrl = Zend_Controller_Front::getInstance()->getBaseUrl();
}
public function progressMeter() {
$html='';
$html.='<span id="progressWrapper">';
$html.='<span id="progressMeter"></span>';
$html.='</span>';
$html.='';
return $html;
}
}
You are calling your class without instantiating it.
Your generalFunctions() function uses the $this pointer, which won't work; also it isn't a static method.
One option is set progress meter to be a static function and call it directly like this:
Zend_View_Helper_GeneralFunctions::progressMeter();
Another option is to instantiate your class first.
Related
Can anyone explain why the below code works, at least for the protected method call:
<?php
class Component
{
public function callMethod($obj, $method)
{
return $obj->$method();
}
}
class Component1 extends Component
{
public function aPublicMethod()
{
return 'called aPublicMethod()';
}
protected function aProtectedMethod()
{
return 'called aProtectedMethod()';
}
private function aPrivateMethod()
{
return 'called aPrivateMethod()';
}
}
class Component2 extends Component
{}
$component1 = new Component1();
$component2 = new Component2();
echo $component2->callMethod($component1, 'aPublicMethod'); // works
echo $component2->callMethod($component1, 'aProtectedMethod'); // works
echo $component2->callMethod($component1, 'aPrivateMethod'); // returns an error because the private method is not accessible
Is it simply because both objects extend from the same parent class, and this contextual information allows Component2 to access Component1's protected methods (and properties)? This was just something I came across with a framework that follows a similar style and was trying to figure out why the second object was still able to access a protected method from another object.
Very good question.
As far as I can tell, its because $component1->aProtectedMethod(); is being called from within a scope that sits within the the inheritance chain (Component1 is a Component polymorphically, where the method is invoked). Which satisfies the requirements for accessing protected scope.
Having access to public scope speaks for itself. So this works anyway.
The error raised by calling $component1->aPrivateMethod(); from outside the class that declares it (outside private scope), also behaves as expected. Only Component1 instances can call aPrivateMethod from withing their encapsulated runtime.
I have a class base which has a property called load which is a object of the class load. The load class has a function called view that includes pages. Now I need to call,
This is similar to CodeIgniter's $this->load->view("test.php");
Load Class
class Load {
public function view($page){
//this function loads views to display
include($page);
}
public function model($class){
//loads model classes
}
public function library($class){
//loads libraries
}
}
Base Class
class Base {
function __construct(){
$this->load = new Load();
}
}
Index page
$base = new Base();
$base->load->view("test1.php");
this1.php
echo "testing1\n";
$this->load->view("test2.php");
test2.php
echo "testing2";
The output should be
testing1
testing2
What you really want I think is to follow a factory pattern. (At least, that's what you mean if you want the $view variable to actually contain an instance of the Load class)
Make the constructor protected, so that the only the class can create new instances, then in the base class add a static method, e.g. 'factory' which returns an instance of the desired class.
Then your code would look like
$view=Base::factory();
$view->view("test1.php");
NOTE: this answer was made before any edit made to the question. Please evaluate accordingly
You need to have the functions marked as public to allow them to be called from outside of the defining class (this is simplified of course)
Try the following:
class Load{
public function view($page){
include($page);
}
}
class Base{
public $load;
function __construct(){
$this->load = new Load();
}
}
(The uppercase class names are my own preference)
This should work, but it's not a good design from a clean OOP perspective, because the users of the Base class need to know how the Load class works. This is called "tight coupling" and should be avoided as much as possible.
I suggest to consider the following alternative:
class Load{
public function view($page){
include($page);
}
}
class Base{
private $load; //note the private modifier
function __construct(){
$this->load = new Load();
}
public function view($page){
$this->load->view($page);
}
}
This way I just need to know that Base has a method view($page) and i don't have to know anymore what Load does at all.
If in the future you want to change the Load class you can do it under the hood without the Base users ever noticing it, if you do it right:
Suppose you define a class:
class BetterLoad {
private function foo(){
//do something awesome
}
private function advancedView($page){
include($page);
$this->foo();
}
}
and you want to incorporate this inside Base instead of the old Load.
class Base{
private $adv_load; //note the private modifier
function __construct(){
$this->adv_load = new BetterLoad();
}
public function view($page){
$this->adv_load->advancedView($page);
}
}
That's it. You won't need to change anything else in your code. Just go on using the old $base_obj->view($page) and you're good to go, without even noticing the change.
I'm struggling to get my Behavior class to use an object instance in the callbacks.
class SomethingBehavior extends ModelBehavior
{
public function setObject($obj)
{
// do stuff
}
public function afterFind(Model $model,$results,$primary)
{
// use the $obj reference set above
}
}
Now I need the Model class to call setObject(..) before any find operations are performed. So ideally I would just assign the object I need in the constructor.
class Document extends AppModel
{
//.....
public function __construct($id,$table,$ids)
{
parent::__construct($id,$table,$ds);
$this->Something->setObject(new MyClass());
}
}
My problem is that the Behavior object isn't yet configured, and I get a not an object error when trying to use it.
I can't find any callback method for Models like in Components. For example, there is no setup or initialize method.
How can I assign the object I need to the Behavior?
You don't seem to have worked with behaviors much. Try to use the containable, tree or other core or plugin behaviors, then you will soon figure out the basics.
First of all, behaviors are attached to models (and since 2.3: loaded), not the other way around. A model then gets "richer" in functionality.
Either statically be using public $actsAs or dynamically using
$this->Behaviors->attach('Something'); // since 2.3: load() instead of attach()
It can directly access the behavior methods. Lets say we have a method foo() in your behavior.
You can then call it from your model as
$this->foo($foo, $bar);
Or from your controller as
$this->Document->Behaviors->attach('Something')
$this->Document->foo($foo, $bar);
Awesome, right?
The behavior method usually has this declaration:
public function foo(Model $Model, $foo, $bar) {
$alias = $Model->alias;
// do sth
}
As you can see, you always pass the model into it implicitly (as first argument automatically passed).
You can access all its attributes.
And do not touch the constructor of the model. no need to do that.
If you really need to pass an object in at runtime, why does your approach not work?
public function setObject(MyClass $obj) {
$this->Obj = $obj;
}
Now you can internally use the object from your behavior methods
public function doSth(Model $Model) {
$this->Obj->xyz();
}
Also this might not be the most elegant approach.
You never set the something member of the Document class. You either need to instantiate it inside the constructor, or pass it in.
Personally, I would do something like this:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
$this->behavior->setObject(new MyClass());
}
}
$doc = new Document(..., new SomethingBehavior());
Or better yet, you could even separate it further by doing:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
}
}
$behavior = new SomethingBehavior();
$behavior->setObject(new MyClass());
$doc = new Document(..., $behavior);
That way, there is less magic going on in the constructor.
In other OO languages like Java we can override a function, possible using keywords/annotations like implements, #override etc.
Is there a way to do so in PHP? I mean, for example:
class myClass {
public static function reImplmentThis() { //this method should be overriden by user
}
}
I want user to implement their own myClass::reImplementThis() method.
How can I do that in PHP? If it is possible, can I make it optional?
I mean, if the user is not implementing the method, can I specify a default method or can I identify that the method is not defined (can I do this using method_exists)?
<?php
abstract class Test
{
abstract protected function test();
protected function anotherTest() {
}
}
class TestTest extends Test
{
protected function test() {
}
}
$test = new TestTest();
?>
This way the class TestTest must override the function test.
Yes, there is. You have the option to override a method by extending the class and defining a method with the same name, function signature and access specifier (either public or protected) it had in the base class. The method should not be declared abstract in the base class or you will be required to implement it in the derived class. In you example it would look something like this:
class MyClass {
public static function reImplmentThis() { //this method should be overriden by user
}
}
class MyDerivedClass extends MyClass {
public static function reImplmentThis() { //the method you want to call
}
}
If the user does not overrides it, MyDerivedClass will still have a reImplmentThis() method, the one inherited from MyClass.
That said, you need to be very careful when invoking extended static methods from your derived class to stay out of trouble. I encourage you to refactor your code to extend instance methods unless you have a very specific need to extend static classes. And if you decide there is no better way than extending static classes please be sure to understand Late Static Binding pretty well.
Yes, its possible to check if the method is implemented or not and get a whole lot more of information about a class using PHP Reflection.
This touches on several OOP subjects.
First, simply overriding an method declared in a parent class is as simple as re-declaring the method in an inheriting class.
E.g:
class Person {
public function greet(string $whom) {
echo "hello $whom!";
}
}
class Tommy extends Person {
public function greet(string $whom = "everyone") {
echo "Howdy $whom! How are you?";
}
}
$a = new Tommy();
$a->greet('World');
// outputs:
// Howdy World! How are you?
If on the overriding method you wan to reuse the logic of the overriden one, it's just a matter of calling the parent's method from the extending class::
class Tommy
{
public function greet(string $whom)
{
// now with more emphasis!!!
echo parent::greet(strtoupper($whom)) . "!!!!";
}
}
Now Tommy::greet() calls Person::greet(), but modifies the result before returning it.
One thing to note is that overriding methods have to be compatible with the overriden one: the method visibility can't be more restrictive than the original one (it's OK to increase visibility), and the number and type of required arguments can't conflict with the original delcaration.
This works, because the type of the arguments does not clash with the original, and we have less required arguments than on the parent:
class Leo extends Person {
public function greet(string $whom = "gorgeous", string $greet = "Whatsup" ) {
echo "$greet $whom. How are you?";
}
}
But this doesn't, since there are additional required arguments. This would make impossible to switch the original class for this one transparently, and thus would throw a Warning:
class BadBob extends Person {
public function greet(string $whom, string $greet ) {
echo "$greet $whom. How are you?";
}
}
Additionally, you mention in your question that "this method should be overriden by the user". If you require client classes to actually implement the method, you have a couple of options:
Abstract classes & methods
These are methods where the implementation is left empty, and that extending classes have to implement to be valid. In we changed our original class Person to:
abstract class Person {
public function greet(string $whom) {
echo "hello $whom!";
}
public abstract function hide();
}
Since now the class contains an abstract method, it needs to be declared as an abstract class as well.
Now it is not possible to instantiate Person directly, you can only extend it in other classes.
Now all our existing Person extending classes would be wrong, and trying to execute the previous code would throw a fatal error.
An example of a valid class extending Person now would be:
class Archie extends Person {
public function hide() {
echo "Hides behind a bush";
}
}
Any class that extends Person must declare a public hide() method.
Interfaces
Finally, you mention interfaces. Interfaces are contracts that implementing classes have to fulfill. They declare a group of public methods without an implementation body.
E.g.:
interface Policeman {
public function arrest(Person $person) : bool;
public function help($what): bool;
}
Now we could have class that extended Person and implemented Policeman:
class Jane extends Person implements Policeman {
public function hide() {
echo "Jane hides in her patrol-car";
}
public function arrest(Person $person): bool{
// implement arrest method
return false;
}
public function shoot($what): bool {
// implements shoot() method
return false;
}
}
Importantly, while it's possible to extend only one class (there is no multiple inheritance in PHP), it is possible to implement multiple interfaces, and the requirements for each of those have to be fulfilled for the class to be valid.
Is there anyway (or a pattern) to enforce a call to a parent method?
I have an abstract class like so:
abstract class APrimitive{
public function validate(){
//Do some stuff that applies all classes that extend APrimitive
}
}
Then I have classes that extend upon the APrimitive "base":
class CSophisticated extends APrimitive{
public function validate(){
//First call the parent version:
parent::validate();
//Then do something more sophisticated here.
}
}
The problem is that if we come back to the code in a few months time, and create a few more classes like CSophisticated with a validate() method, there is a possibility that we might forget to make a call to parent::validate() in that method.
Note that some CSophisticated classes may not have the validate() method, so the parent version will be called.
I understand that it is possible to just put in a comment somewhere, to remind the programmer to call parent::validate(), but is there a better way? Perhaps an automated way to throw an exception if the call to parent::validate() was not made in the validate() method would be nice.
You can enforce the call with the following:
abstract class APrimitive{
final public function validate(){
//do the logic in validate
overrideValidate();
}
protected function overrideValidate(){
}
}
class CSophisticated extends APrimitive{
protected function overrideValidate(){
}
}
Now only calls to validate are permitted, which will in turn call your overridden method. The syntax may be a little off (PHP is not my language of choice) but the principle is applyable to most OOP languages.
FURTHER EXPLANATION:
abstract class APrimitive{
public function validate(){
echo 'APrimitive validate call.';
overrideValidate();
}
protected function overrideValidate(){
}
}
class CSophisticated extends APrimitive{
protected function overrideValidate(){
echo 'CSophisticated call.';
}
}
CSophisticated foo;
foo.overrideValidate(); //error - overrideValidate is protected
foo.validate(); //
Output:
APrimitive validate call.
CSophisticated call.
The function call basically does the following:
foo.validate() -> APrimitive.validate() -> ASophisticated.overrideValidate() (or APrimitive.overrideValidate() if it wasn't overriden)
You're looking for The Template Method pattern.
This pattern allows you to modify an operation in some way through sub-classing but ensures that the base class is always involved.
class Base {
//declared final so it can't be overridden
public final function validate() {
//perform base class operations here
//then forward to the sub class
$this->doValidate();
//do some more base class stuff here if needed
}
//override this method to alter validate operation
protected function doValidate(){
//no-op in base
}
}
class Sub {
protected function doValidate() {
//if required
//make the sub-class contribution to validate here
}
}