Is it possible to create a PHP class that would act like this:
class Foo
{
function __construct($param)
{
if (!is_numeric($param))
{
// stop class
}
}
}
$a = new Foo(2);
$b = new Foo('test');
var_dump($a);
var_dump($b);
which will return
object(Foo)[1]
null
The only way I'm aware of to stop creating of a new object while not immediately terminating the script is to throw an exception:
class Foo {
public function __construct($param) {
if (!is_numeric($param)) {
throw new \InvalidArgumentException('Param is not numeric');
}
...
}
Of course, you'd have to be sure and catch the exception in the calling code and handle the problem appropriately.
Create a static create($param) that returns a new instance or null if $param is invalid. You could also consider using Exceptions.
You could try to throw an exception and catch it from another function in the same scope as the variable declaration:
class Foo
{
function __construct($param)
{
if( !is_numeric($param) )
return true;
else
throw new Exception();
}
}
function createFooObject($v){
try{ $x = new Foo($v); return $x; }
catch(Exception $e){
unset($x);
}
}
$a = createFooObject(2);
$b = createFooObject('test');
var_dump($a);
var_dump($b);
Just return null if the parameter is not numeric:
<?php
class Foo{
public function __construct($param = null){
if( !is_numeric($param) ){
return null;
}
}
}
?>
pretty much as you have it:
<?
public class newClass {
public __CONSTRUCT($param = false){
if(!is_numeric($param)){
return false
}
}
}
$class = new newClass(1);
if($class){
//success / is a number
}else{
// fail, not a number, so remove the instance of the class
unset($class);
}
?>
Setting $param = false inside the arguments for the constructor will tell the script to set it to false if there is no input
Related
I basically need to call one of two constructors from my PHP class dependent on wheter or not verification is needed.
My current code looks like this:
class Event extends Generic {
private $user_id;
function __construct($user_id=null) {
$this->user_id = $user_id;
}
public function verify($user_id=null, $identifier=null) {
if (parent::verifyUser($user_id, $identifier)) {
return new Event($user_id);
}
else {
// what gets retruend here in the event of a failure???
}
}
public function noverify($user_id=null) {
return new Event(user_id);
}
}
Calling the code like so:
$event = new Event();
$obj1 = $event->noverify(5);
$obj2 = $event->verify(5, 100);
I'm really not clear how I should handle the event of a failed constructor if the condition inside fails. Am I heading down the wrong path here?
I would either throw an Exception or return FALSE:
else {
throw new Exception('verification failed');
}
or
else {
return FALSE;
}
I need to know do we have to check input parameters of class methods in PHP OOP or not?
For example imagine the method below:
public function show_message($message)
{
echo $message;
}
What is the best solution if programmer don't pass the message parameter to method? Let PHP to show it's warning or run time error or do something else ?
The "best" solution depends on what you want the method to do exactly, but generally, I'd suggest a combination of type-hinting and default values:
class Foo
{
public function doSomething ($message = 'None')
{
echo $message;
}
public function somethingElse (array $foo = array())
{
echo '<pre>';
print_r($foo);
echo '</pre>';//will always be an array
}
public function onlyMyself (Foo $instance)
{
return $instance->doSomething('I know this method exists');
}
public function myselfOrNothing(Foo $instance = null)
{
if ($instance === null)
{
return $this->doSomething('No instance provided');
}
return $instance->doSomething('Instance provided to '.__METHOD__);
}
}
$foo = new Foo;
$bar = new Bar;
$foo->onlyMyself($bar);//works fine
$foo->onlyMyself(array());//fails
$bar->myselfOrNothing();//works
$bar->somethingElse();//ok...
etcetera, you get the basic principle.
Note that, if you're using an abstract parent class (or just any old parent class), type-hinting a parent allows for the child to be passed, too:
class Bar extends Foo
{
}
class Foobar extends Bar
{
}
$fb = new Foobar;
$b = new Bar;
public function someClass(Foo $instance)
{//accepts instances of Foo, Bar and Foobar...
echo get_class($instance);//can echo any class name
}
Allow for a default value, then trap for that default. This puts control of what you do in your hands rather than the simple default PHP behaviour
public function show_message($message = "\x00")
{
if ($message === "\x00") {
// decide how critical this argument actually is, and react appropriately
throw new BadMethodCallException("The message argument is critical and must be passed to this method");
// or simply apply a default if it isn't critical
$message = 'Hello World';
}
echo $message;
}
I think the type of error should depend on how important the function is and if that is enough of a reason to stop execution if the parameter is not there.
if you are talking about input parameters validation. you can do something like this.
public function show_message($message = '') {
$result = 'No message';
if (!empty($message)) {
if (is_bool($message)) {
$result = 'It is a boolean';
}
if (is_int($message)) {
$result = 'It is a integer';
}
if (is_float($message)) {
$result = 'It is a float';
}
if (is_string($message)) {
$result = 'It is a string';
}
if (is_array($message)) {
$result = 'It is an array';
}
if (is_object($message)) {
$result = 'It is an object';
}
}
return $result;
}
PHP calls private method in parent class instead of method define in current class called by call_user_func
class Car {
public function run() {
return call_user_func(array('Toyota','getName')); // should call toyota
}
private static function getName() {
return 'Car';
}
}
class Toyota extends Car {
public static function getName() {
return 'Toyota';
}
}
$car = new Car();
echo $car->run(); //Car instead of Toyota
$toyota = new Toyota();
echo $toyota->run(); //Car instead of Toyota
I have found a solution with a different approach..
<?php
class Car {
public static function run() {
return static::getName();
}
private static function getName() {
return 'Car';
}
}
class Toyota extends Car {
public static function getName() {
return 'Toyota';
}
}
echo Car::run();
echo Toyota::run();
?>
Using Late Static Binding..
You might use something like this:
<?php
class Car {
public function run() {
return static::getName();
}
private static function getName(){
return 'Car';
}
}
class Toyota extends Car {
public static function getName(){
return 'Toyota';
}
}
$car = new Car();
echo $car->run();
echo PHP_EOL;
$toyota = new Toyota();
echo $toyota->run();
?>
Output:
Car
Toyota
PHP 5.4.5
This is a bug that appears to have fluctuated in and out of existence for a long time (see #deceze's tests in comments on the question). It is possible to "fix" this issue - that is, give consistent behaviour across PHP versions - using reflection:
Works in PHP 5.3.2 and later due to a dependency on ReflectionMethod::setAccessible() to invoke private/protected methods. I will add further explanation for this code, what it can and can't do and how it works very shortly.
Unfortunately it's not possible to test this directly on 3v4l.org because the code is too large, however this is the first ever real use case for minifying PHP code - it does work on 3v4l if you do this, so feel free to play around and see if you can break it. The only issue I'm aware of is that it doesn't currently understand parent. It is also restricted by the lack of $this support in closures before 5.4, there's not really anything that can be done about this though.
<?php
function call_user_func_fixed()
{
$args = func_get_args();
$callable = array_shift($args);
return call_user_func_array_fixed($callable, $args);
}
function call_user_func_array_fixed($callable, $args)
{
$isStaticMethod = false;
$expr = '/^([a-z_\x7f-\xff][\w\x7f-\xff]*)::([a-z_\x7f-\xff][\w\x7f-\xff]*)$/i';
// Extract the callable normalized to an array if it looks like a method call
if (is_string($callable) && preg_match($expr, $callable, $matches)) {
$func = array($matches[1], $matches[2]);
} else if (is_array($callable)
&& count($callable) === 2
&& isset($callable[0], $callable[1])
&& (is_string($callable[0]) || is_object($callable[0]))
&& is_string($callable[1])) {
$func = $callable;
}
// If we're not interested in it use the regular mechanism
if (!isset($func)) {
return call_user_func_array($func, $args);
}
$backtrace = debug_backtrace(); // passing args here is fraught with complications for backwards compat :-(
if ($backtrace[1]['function'] === 'call_user_func_fixed') {
$called = 'call_user_func_fixed';
$contextKey = 2;
} else {
$called = 'call_user_func_array_fixed';
$contextKey = 1;
}
try {
// Get a reference to the target static method if possible
switch (true) {
case $func[0] === 'self':
case $func[0] === 'static':
if (!isset($backtrace[$contextKey]['object'])) {
throw new Exception('Use of self:: in an invalid context');
}
$contextClass = new ReflectionClass($backtrace[$contextKey][$func[0] === 'self' ? 'class' : 'object']);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
break;
case is_object($func[0]):
$contextClass = new ReflectionClass($func[0]);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if ($method->isStatic()) {
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
} else {
$invokeContext = $func[0];
}
break;
default:
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
$method = new ReflectionMethod($func[0], $func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if (empty($backtrace[$contextKey]['object'])
|| $func[0] !== $contextClass->getName()
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
if (empty($backtrace[$contextKey]['object']) || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method outside a class context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
break;
}
// Invoke the method with the passed arguments and return the result
return $method->invokeArgs($invokeContext, $args);
} catch (Exception $e) {
trigger_error($called . '() expects parameter 1 to be a valid callback: ' . $e->getMessage(), E_USER_ERROR);
return null;
}
}
Use "protected" modifier if you want to get access from parent and descendants only. IMO, it's obvious. For example:
<?php
class Car {
public function run() {
return call_user_func(array('static','getName'));
}
protected static function getName() {
return 'Car';
}
}
class Toyota extends Car {
protected static function getName() {
return 'Toyota';
}
}
$car = new Car();
echo $car->run(); // "Car"
$toyota = new Toyota();
echo $toyota->run(); // "Toyota"
You can use get_called_class() instead of 'static'.
The problem is, I think, with the different access levels of the two getname functions. If you make the base class version of getname() public (the same as the derived class version), then in php 5.3.15 (on my Mac), you get Toyota. I think that, because of the different access levels, you end up with two different versions of the getname() function in the Toyota class, rather than the derived class version overriding the base class version. In other words, you have overloading rather than overriding. Therefore, when the run() function looks for a getname() function in the Toyota class to execute, it finds two and takes the first one, which would be the first to be declared (from the base class).
Granted this is just supposition on my part, but it sounds plausible.
use the get_called_called function todo this
public function run() {
$self = get_called_class();
return $self::getName();
}
I believe you're functions are overriding each other and by default going to the first one. Unless you change the parameters of one function, or rename the function it will always default to the parent class function.
How can I let the $foo variable below know that foo should be false?
class foo extends fooBase{
private
$stuff;
function __construct($something = false){
if(is_int($something)) $this->stuff = &getStuff($something);
else $this->stuff = $GLOBALS['something'];
if(!$this->stuff) return false;
}
}
$foo = new foo(435); // 435 does not exist
if(!$foo) die(); // <-- doesn't work :(
You cannot return a value from the constructor. You can use exceptions for that.
function __construct($something = false){
if(is_int($something)) $this->stuff = &getStuff($something);
else $this->stuff = $GLOBALS['something'];
if (!$this->stuff) {
throw new Exception('Foo Not Found');
}
}
And in your instantiation code:
try {
$foo = new foo(435);
} catch (Exception $e) {
// handle exception
}
You can also extend exceptions.
Constructor is not supposed to return anything.
If you need to validate data before using the to create an object, you should use a factory class.
Edit: yeah , exceptions would do the trick too, but you should not have any logic inside the constructor. It becomes a pain for unit-testing.
You can try
<?php
function __construct($something = false){
$this->stuff = $something;
}
static function init($something = false){
$stuff = is_int($something) ? &getStuff($something) : $GLOBALS['something'];
return $stuff ? new self($stuff) : false;
}
$foo = foo::init(435); // 435 does not exist
if(!$foo) die();
In effect, if I have a class c and instances of $c1 and $c2
which might have different private variable amounts but all their public methods return the same values I would like to be able to check that $c1 == $c2?
Does anyone know an easy way to do this?
You can also implement a equal($other) function like
<?php
class Foo {
public function equals($o) {
return ($o instanceof 'Foo') && $o.firstName()==$this.firstName();
}
}
or use foreach to iterate over the public properties (this behaviour might be overwritten) of one object and compare them to the other object's properties.
<?php
function equalsInSomeWay($a, $b) {
if ( !($b instanceof $a) ) {
return false;
}
foreach($a as $name=>$value) {
if ( !isset($b->$name) || $b->$name!=$value ) {
return false;
}
}
return true;
}
(untested)
or (more or less) the same using the Reflection classes, see http://php.net/manual/en/language.oop5.reflection.php#language.oop5.reflection.reflectionobject
With reflection you might also implement a more duck-typing kind of comparision, if you want to, like "I don't care if it's an instance of or the same class as long as it has the same public methods and they return the 'same' values"
it really depends on how you define "equal".
It's difficult to follow exactly what you're after. Your question seems to imply that these public methods don't require arguments, or that if they did they would be the same arguments.
You could probably get quite far using the inbuilt reflection classes.
Pasted below is a quick test I knocked up to compare the returns of all the public methods of two classes and ensure they were they same. You could easily modify it to ignore non matching public methods (i.e. only check for equality on public methods in class2 which exist in class1). Giving a set of arguments to pass in would be trickier - but could be done with an array of methods names / arguments to call against each class.
Anyway, this may have some bits in it which could be of use to you.
$class1 = new Class1();
$class2 = new Class2();
$class3 = new Class3();
$class4 = new Class4();
$class5 = new Class5();
echo ClassChecker::samePublicMethods($class1,$class2); //should be true
echo ClassChecker::samePublicMethods($class1,$class3); //should be false - different values
echo ClassChecker::samePublicMethods($class1,$class4); //should be false -- class3 contains extra public methods
echo ClassChecker::samePublicMethods($class1,$class5); //should be true -- class5 contains extra private methods
class ClassChecker {
public static function samePublicMethods($class1, $class2) {
$class1methods = array();
$r = new ReflectionClass($class1);
$methods = $r->getMethods();
foreach($methods as $m) {
if ($m->isPublic()) {
#$result = call_user_method($m->getName(), $class1);
$class1methods[$m->getName()] = $result;
}
}
$r = new ReflectionClass($class2);
$methods = $r->getMethods();
foreach($methods as $m) {
//only comparing public methods
if ($m->isPublic()) {
//public method doesn't match method in class1 so return false
if(!isset($class1methods[$m->getName()])) {
return false;
}
//public method of same name doesn't return same value so return false
#$result = call_user_method($m->getName(), $class2);
if ($class1methods[$m->getName()] !== $result) {
return false;
}
}
}
return true;
}
}
class Class1 {
private $b = 'bbb';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class2 {
private $a = 'aaa';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class3 {
private $c = 'ccc';
public function one() {
return 222;
}
public function two() {
return "bendy";
}
}
class Class4 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
public function three() {
return true;
}
}
class Class5 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
private function three() {
return true;
}
}
You can define PHP's __toString magic method inside your class.
For example
class cat {
private $name;
public function __contruct($catname) {
$this->name = $catname;
}
public function __toString() {
return "My name is " . $this->name . "\n";
}
}
$max = new cat('max');
$toby = new cat('toby');
print $max; // echoes 'My name is max'
print $toby; // echoes 'My name is toby'
if($max == $toby) {
echo 'Woohoo!\n';
} else {
echo 'Doh!\n';
}
Then you can use the equality operator to check if both instances are equal or not.
HTH,
Rushi
George: You may have already seen this but it may help: http://usphp.com/manual/en/language.oop5.object-comparison.php
When using the comparison operator (==), object variables are compared in a simple manner, namely: Two object instances are equal if they have the same attributes and values, and are instances of the same class.
They don't get implicitly converted to strings.
If you want todo comparison, you will end up modifying your classes. You can also write some method of your own todo comparison using getters & setters
You can try writing a class of your own to plugin and write methods that do comparison based on what you define. For example:
class Validate {
public function validateName($c1, $c2) {
if($c1->FirstName == "foo" && $c2->LastName == "foo") {
return true;
} else if (// someother condition) {
return // someval;
} else {
return false;
}
}
public function validatePhoneNumber($c1, $c2) {
// some code
}
}
This will probably be the only way where you wont have to modify the pre-existing class code