Using PHP function_exists in a class? - php

I am using OpenCart and installed the module sizes.xml, it edits the catalog/product/model/catalog/product.php file
It adds a class method;
I have an issue with some code where in a function I have:
class ModelCatalogProduct extends Model {
public function getSizes($product_id) {
...
} // end function
} // end class
We always get an error saying that we cannot redeclare getSizes method.
I want to either force redeclaration, or alternatively check whether the function exists before calling the method but I cannot use function_exists in a class.
ie, this seems illegal;
class ModelCatalogProduct extends Model {
// This throws up an error
if (function_exists('getSizes')==FALSE) {
public function getSizes($product_id) {
} // end function
}// end if
} // end class
It will throw up an error issue. But I need the function in there.
I would like to use function_exists in this class or alternatively force the method to be redeclared.
How would I make function_exists work inside a class?

You can't have executable code in a class that is outside of a method, so there's no way you can do what you're asking, as your if() condition would need to be in the class body.
So despite what others are saying, method_exists() is not a suitable answer to this question.
If you're getting an error stating that the method is already declared, then there are a few possible reasons for this:
It is actually already declared elsewhere in the same class. In which case, of course you can't redeclare it. But since the code for the class ought to all be in a single file, then it should be fairly easy to see that and avoid doing it.
It's declared in the parent class (ie in your case Model or one of its parents).
Under normal circumstances, you should be able to redeclare a method that is already declared in a parent class; your method would override the method of the same name from the parent class. So for most cases, your whole question is entirely unnecessary.
But you say you're getting errors, so clearly something is going wrong It would help if you'd told us the exact error message, but there are two reasons I can think of this might not work:
If the method in the parent class is declared as Final, then it means the author of the parent class explicitly doesn't want it to be overridden. This means that you cannot have your own method of the same name.
If the method in the parent class has a different signature - eg it's private in the parent but public in your class, or static in one but not in the other, then you will get errors complaining about that. In this case, you'll need to make sure that the methods have the same signature, or else give your method a different name.
Hope that helps.

The answer of #Spudley is correctly chosen as the right answer.
Just more explanation on the first case mentioned in the answer: if you wand to declare the function inside of a method of a class, you should also consider the namespace of the class:
class MyClass
{
function myFunction()
{
//here, check if the function is defined in the root namespace globally
// or in the current namespace:
if(!function_exists('someFunction')
&& !function_exists(__NAMESPACE__ . '\someFunction'))
{
function someFunction()
{
}
}
//....
someFunction();
}
}
If you don't check the second condition of if, calling myFunction() more than once would throw exception

Related

Snookered by abstract - PHP

I have an abstract class I am inheriting from:
abstract class Test
{
public function GetTests()
{
}
}
and I have a concrete that I have been using the abstract classes implementation for most of the time:
class Concrete extends Test
{
// No problemmos
}
I recently had to implement a different version of the GetTests method, and in fact I wanted to overwrite it as it's built into all of my routing:
class Concrete extends Test
{
public function GetTests( $newArgument )
{
// notice $newArgument
}
}
However I get this error message:
Declaration of Concrete::GetTests() should be compatible with Test::GetTests()
Apart from copying the entirety of the functions from the abstract class for this concrete, even though I only need to implement this one method differently... Is there a way of getting around this?
I do understand that I could have:
abstract class Test
{
abstract public function GetTests();
}
But this is why I am snookered, because I no longer have the ability to modify how the underlying Test class is implemented... doh!... Unless I really have to..
Thanks to all great answers...
I have decided to de snooker myself (it's going to hurt but it's going to be worth it) and I will instantiate the Test class inside the Concrete class, implement concrete versions of all the Test class methods, and then inside them just call the instantiated Test class... This means in the future (or indeed now) I can simply not call that feature...
For context:
/* no longer abstract */ class UnitOfWorkController
{
public function GetUnits()
{
// Implementation
return View::make(...);
}
}
and...
class SomethingController /* no longer extends the UnitOfWorkController */
{
private $unitOfWorkController;
public function __Construct()
{
$this->unitOfWorkController = new UnitOfWorkController();
}
public function GetUnits()
{
return $this->unitOfWorkController->GetUnits();
// or I could just implement my own junk
}
}
Your concrete subclass is in violation of the Liskov Substitution Principle, which to cut a long story short says that if an object of class X can be processed by a given piece of code, then every possible subclass of X must also be able to be processed by the same piece of code.
Say I wanted to make another subclass of Test and wanted to implement my own GetTests method. The base class method doesn't accept any arguments at all, so that suggests that, if my subclass is to be substitutable for its superclass, my implementation of that method cannot take any arguments either. If I give my implementation arguments, then it no longer conforms to the specification as laid down by the superclass.
If I have code that does:
$object = new Test;
$test -> GetTests ();
then I can't substitute my subclass of Test without also changing the calling code to pass in an argument. Likewise if I do change it, then I have another subclass of Test that doesn't require an argument for GetTests then the code would have to change again. In fact the same code simply can't be used as is with both subclasses without having to jump through some hoops to determine the actual class and using the appropriate calling convention which means needing to know things about the class I'm about to use that I shouldn't need to know.
PHP is less strict than most OO languages about subclass method signitures matching their superclass, but it will issue a warning if they don't match. The only way to fix the warning is to have all subclasses have the same method signatures as the superclass they inherit from.
Child methods must have the same signature as the same method in a parent class. This includes required parameters and their typecasting.
For example, a child class of the following method must also have one parameter, and the parameter must cast to the ArgumentType class or a child of thereof.
public function something(ArgumentType $Argument)
{
}
You can, however, make the parameter optional by setting it to null or any other value:
public function something(ArgumentType $Argument = null)
{
}
In this case, child methods may omit this parameter.
From the PHP docs, see http://php.net/manual/en/language.oop5.abstract.php:
[…] Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. For example, if the child class defines an optional argument, where the abstract method's signature does not, there is no conflict in the signature.
The method signature of Concrete::GetTests() has a variable while Test::GetTests() does not. Since you have already defined this method within Test, it is now being inherited. The inherited version is not compatible with your overridden version.
Here are your options:
Add $newArgument to the parameters list in Test::GetTests().
Remove $newArgument from the parameters list in Concrete::GetTests().
Rename Concrete::GetTests() to something else.
PHP does not support this, as the error message says. If you want to override the function, it has to have the same footprint, which in your case it doesn't
What you could do is use a magic method: http://php.net/manual/en/language.oop5.overloading.php#object.call
the parameter array is a separate entity, so you 'decide' in your code (which you can override) what to do with which parameter.
I wanted to link a blog I read about this, but couldn't find the one I was thinking of. There is this rather strangely formatted one, not sure if it any good, but it does touch on some of the issues.
You could obviously add the argument to the parent, but this is 'leaking' upwards. If other childeren want even more, you'd get a big party of random paramteres that all can be nulled.

PHP: Calling a function whenever a function in a child class is called

Within a single class, I am able to use the _call/_callStatic functions to call a function before any function is actually run. However, I'm yet to find a solution that I can apply to the parent, and it will be inherited by the children.
The only method I can think of is putting a small snippet of code within each class that calls a function in the parent, which contains my code.
Are there any better solutions that I can implement?
As long as you are at least on php 5.4, you could use traits.
http://php.net/traits
This will need some adjustments in your code but could allow the desired behavior.
Do you have some more information on your use case? I think of logging from your description, is this correct?
Unfortunately there are not magic methods in PHP which get run whenever any method is called.
I think the solution you are looking for is a decorator, see this article for the tips on decorating methods / classes in PHP.
how to implement a decorator in PHP?
It's hard to help you without seeing some code. However, assuming that you don't re-define __call/__callStatic in the subclass, it should simply inherit the method from it's superclass.
If you do re-define __call/__callStatic in the subclass, it overrides the superclass' definition. So you'd somehow have to call the superclass' method. In order to do this you can use the parent keyword. See this example:
class SuperClass
{
public function __call($name, $arguments)
{
// Do some stuff
}
}
class SubClass extends SuperClass
{
public function __call($name, $arguments)
{
// Execute the parent's __call() method
return parent::__call($name, $arguments);
// Do some extra stuff here
}
}

Calling static method non-statically

I have a child class that extends a class with only static methods. I would like to make this child class a singleton rather than static because the original developer really wanted a singleton but used static instead (obvious because every method in the static class calls the Init() function (basically a constructor)).
Most of the methods in the parent don't need to be overwritten in the child, but I would like to avoid having to write methods like this:
public function Load($id)
{
return parent::Load($id);
}
when I would prefer not to overwrite the method at all and just use:
$child->Load($id);
Is it possible to call a static method non-statically? Is it possible to extend a static object with an instance object? I know I can try it and it will likely work (PHP is very forgiving), but I don't know if there is anything I should be concerned about.
Can you inherit static methods?
Yes
Can you override static methods?
Yes, but only as of PHP 5.3 do they work as you would expect: http://www.php.net/manual/en/language.oop5.static.php (ie. self binds to the actual class and not the class it's defined in).
Is it possible to call a static method non-statically?
Yes, but will lose $this. You don't get a warning (yet) but there also isn't really a reason to call it the wrong way.
Two part answer.
First, about the titular question: calling a static method non-statically is perfectly fine; #SamDark's comment is correct. It does not produce a warning, nor does it cause any kitten murdering. Try it:
<?php
class test {
public static function staticwarnings(){
echo "YOU ARE (statically) WARNED!\n";
}
}
error_reporting(E_ALL);
$test = new test();
echo "\n\ncalling static non-statically\n";
$test->staticwarnings();
If you had an instance reference, $this, in that static method, then you would get a fatal error. But that is true regardless of how you call it.
Once again, there isn't a warning, nor any kitten killed.
Second part of the answer:
Calling an overridden parent function from an overriding child class requires something called "scope resolution". What the OP is doing in their method is NOT calling a static method. (Or at least, it doesn't have to be; we can't see the parent implementation). The point is, using the parent keyword is not a static call. Using the :: operator on an explicit parent class name is also not a static call, if it is used from an extending class.
Why is that documentation link so strangely named? It's literally Hebrew. If you've ever run into an error related to it, you might have observed the delightfully-named parser error code T_PAAMAYIM_NEKUDOTAYIM.

Using PHPUnit to mock a programatically determined method not specified in the class under test

Using PHPUnit 3.6 I'm trying to test the exec() method in the below controller class. This method does two things:
Determines the name of the method to call based on the object's existing properties, and ...
If the determined controller method is callable it is executed and if not the method throws an exception
The (simplified) source code looks like this:
abstract class CLIController extends Controller
{
/* irrelevant class details here */
public function exec()
{
$action = ! empty($this->opts->args[0])
? $this->opts->args[0]
: $this->default_action;
if ( ! $action || ! is_callable(array($this, $action))) {
$msg = 'Invalid controller action specified';
throw new LogicException($msg);
} else {
$this->$action(); // <---- trying to get code coverage on this line!
}
}
}
So my problem is ...
I can't figure out how to get coverage on this part of the code:
} else {
$this->$action();
}
because I'm not sure how to (or that it's even possible to) test the invocation of a method whose name is not known in the context of the abstract class. Again: the method to be called is declared in child classes. Normally I would just mock an abstract method but I can't in this case because the method doesn't exist yet -- it will be specified by a child class.
What might be the answer ...
??? It may be possible that this line doesn't even need to be covered because it essentially relies on PHP's ability to correctly invoke a callable class method. If I successfully test that exec() throws an exception when it's supposed to, I know that correct functioning of the line in question depends on PHP functioning correctly. Does this invalidate the need to test it in the first place ???
If there is some way to mock the abstract class and create a method with a known name to add to the mocked class this would solve my problem and is what I've been trying unsuccessfully to do so far.
I know I could create a child class with a known method name but I don't believe it's a good idea to create a concrete child class just to test an abstract parent.
It could be that I need to refactor. One thing I don't want to do is leave child classes to implement the exec() function on their own.
What I've tried ...
Use some of PHP's reflection capabilities to no avail -- this may perhaps be due to my own inexperience with reflection and not its inability to handle this case, though.
Going back and forth through the PHPUnit manual and API docs. Unfortunately, as awesome as PHPUnit is, I often find the API documentation a bit light.
I would really appreciate any guidance on how best to proceed here. Thanks in advance.
I disagree with your stipulation that "it's [not] a good idea to create a concrete child class just to test an abstract parent." I do this quite often when testing abstract classes and usually name the concrete subclass after the test to make it clear.
class CLIControllerTest extends PHPUnit_Framework_TestCase
{
public function testCallsActionMethod()
{
$controller = new CLIControllerTest_WithActionMethod(...);
// set $controller->opts->args[0] to 'action'
$controller->exec();
self::assertTrue($controller->called, 'Action method was called');
}
}
class CLIControllerTest_WithActionMethod extends CLIController
{
public $called = false;
public function action() {
$this->called = true;
}
}
The code to make this test happen is trivial and can be easily verified by inspection.
I'm curious, why use is_callable instead of method_exists to avoid creating the array? It's probably just personal preference, but I'm wondering if there are any semantic differences.

is it possible to change method property from public to private and back on runtime from inside class?

like this:
if ($sth) make_private($this->method);
or maybe there's some other way to affect accessibility of methods ?
Problem is that I written a class where methods must be called once, so I need code to restrict access to given method from outside the class after this method was executed.
You've got several better options:
Handle the 'can only be called once' with some static state variable in the class itself, and throw legible exceptions.
Handle the 'can only be called once' with a decorator object if you cannot alter the class/object itself.
The very undesirable way you suggest is possible, see classkit_method_redefine or runkit_method_redefine, but on behalf of anyone possibly working on your code in future: please do not use it.
Simple way to do so within the mothod (restrict to one call):
public function fooBar() {
static $called;
if (isset($called)) throw new Exception('Called already once!');
$called = true;
// your code
}

Categories