I have this code. Is it possible for a User object constructor to somehow fail so that $this->LoggedUser is assigned a NULL value and the object is freed after constructor returns?
$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false)
$this->LoggedUser = new User($_SESSION['verbiste_user']);
Assuming you're using PHP 5, you can throw an exception in the constructor:
class NotFoundException extends Exception {}
class User {
public function __construct($id) {
if (!$this->loadById($id)) {
throw new NotFoundException();
}
}
}
$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false) {
try {
$this->LoggedUser = new User($_SESSION['verbiste_user']);
} catch (NotFoundException $e) {}
}
For clarity, you could wrap this in a static factory method:
class User {
public static function load($id) {
try {
return new User($id);
} catch (NotFoundException $unfe) {
return null;
}
}
// class body here...
}
$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false)
$this->LoggedUser = User::load($_SESSION['verbiste_user']);
As an aside, some versions of PHP 4 allowed you to set $this to NULL inside the constructor but I don't think was ever officially sanctioned and the 'feature' was eventually removed.
AFAIK this can't be done, new will always return an instance of the object.
What I usually do to work around this is:
Adding a ->valid boolean flag to the object that determines whether an object was successfully loaded or not. The constructor will then set the flag
Creating a wrapper function that executes the new command, returns the new object on success, or on failure destroys it and returns false
-
function get_car($model)
{
$car = new Car($model);
if ($car->valid === true) return $car; else return false;
}
I'd be interested to hear about alternative approaches, but I don't know any.
Consider it this way. When you use new, you get a new object. Period. What you're doing is you have a function that searches for an existing user, and returns it when found. The best thing to express this is probably a static class function such as User::findUser(). This is also extensible to when you're deriving your classes from a base class.
A factory might be useful here:
class UserFactory
{
static public function create( $id )
{
return (
filter_var(
$id,
FILTER_VALIDATE_INT,
[ 'options' => [ 'min_range' => 1, ] ]
)
? new User( $id )
: null
);
}
}
When a constructor fails for some unknown reason, it won't return a NULL value or FALSE but it throws an exception. As with everything with PHP5. If you don't handle the exception then the script will stop executing with an Uncaught Exception error.
maybe something like this:
class CantCreateException extends Exception{
}
class SomeClass {
public function __construct() {
if (something_bad_happens) {
throw ( new CantCreateException());
}
}
}
try{
$obj = new SomeClass();
}
catch(CantCreateException $e){
$obj = null;
}
if($obj===null) echo "couldn't create object";
//jaz303 stole my idea an wrap it into a static method
Related
This is my code below.
class MyModel extends Model
{
public function __call($method, $parameters = null) {
if($method == 'create'){
return parent::create($parameters[0]);
if(!$created) {
throw new \App\Exceptions\EloquentException;
}else{
return $created;
}
}
}
}
The problem is that when I call update function of MyModel class instance from postman, something bad happens. It gets stuck and I have to restart my computer each time. So what may the problem be?
I'll try to assume you're simply trying to have a common handler for the create function, that is, to throw an EloquentException in case the create returns a null or false.
If that's the case, you have an excess return statement on line above the if statement, and you should assign the return value of the parent's create method to a variable $created that you use later. You may also remove the else part as code below throw is never going to be executed if the exception is thrown.
class MyModel extends Model
{
public function __call($method, $parameters = null)
{
if ($method == 'create') {
$create = parent::create($parameters[0]);
if (!$created) {
throw new \App\Exceptions\EloquentException;
}
return $created;
}
}
}
It would be better if you could elaborate on the task you're trying to achieve, I feel you're doing it the wrong way.
I want to create an object of a class from a returned string but I am getting error Class **test_report** not found. My code:
public function display_report_builder($report_name = null)
{
$column_listing = new $report_name;// gets the test_report
return view('column_list')->with(['column_list_names' => $column_listing->columns]);
}
This isn't the better approach here. What you should do is to use a Factory design pattern:
class ReportFactory
{
public static function create($report_name)
{
switch($report_name) {
case 'test_report': return new TestReport();
default: throw new Exception('report not found');
}
}
}
Then you call with $column_listing = ReportFactory::create($report_name);
Why? Because you avoid "magic variables" with unknown data; you can trace errors properly; you can use namespace; you can extend functionalities easily, and easily activate or deactivate objects (or reports in this case); you have a cleaner code, and so on...
test if the class name (string) really is a valid class :
public function display_report_builder($report_name = null)
{
$column_list_names = null;
if (class_exists($report_name) && is_a($report_name, App\reports\test_report::class, true)) {
$column_listing = new $report_name;
$column_list_names = $column_listing->columns;
}
return view('column_list', compact('column_list_names'));
}
is_a() : Checks if the given object is of this class or has this class
as one of its parents.
[edit] updated the title to more accurately reflect the problem
The problem I am trying to solve is this: I need to know if a method was called via parent:: and while I can use debug_backtrace it seems like there must be a better way to do this.
I've been looking into late static binding but perhaps I don't understand it well enough to fathom a solution.
The method in question is __call so I can't simply pass in an extra parameter as its an error to have more or less then exactly two.
The reason for trying to solve this problem is that the parent class has __call but the child may or may not have _call. If the child doesn't have it, and the parent doesn't dispatch the call, then I'd like to throw an exception or error. If the child does have the method then I'll return false (no we didn't handle this) and let the child _call method carry on.
So far my only working solution is to have the child call parent::__call wrapped in a try/catch block and have the parent throw an exception by default if it does not route the request.
ie.
class Parent {
public function __call( $method, $params ) {
if( preg_match( $this->valid, $method ) {
$this->do_stuff();
// if child has a call method, it would skip on true
return true;
}
elseif( ** CHILD HAS CALL METHOD ** ) {
// this would let the child's _call method kick in
return false;
}
else {
throw new MethodDoesNotExistException($method);
}
}
}
class Child extends Parent {
public function __call( $method, $params ) {
if( ! parent::__call( $method, $params ) ) {
do_stuff_here();
}
}
}
While throwing an exception if the parent doesn't handle the method works, I'm just trying to see if there's a more elegant solution, as using exceptions for flow-controll doesn't seem quite right. But neither does using a stacktrace to figure out the caller, either.
This should do in your parent class:
if (__CLASS__ != get_class($this))
I'm not entirely sure if this fits your needs and I also consider this kind of hacks to be really bad from the OO design point of view. However, it was a fun thing to code :)
<?php
class ParentClass
{
public function __call( $method, $params )
{
if($method === 'one')
{
echo "Parent\n";
return true;
}
elseif($this->shouldForwardToSubclass($method))
{
return false;
}
else
{
throw new Exception("No method");
}
}
protected function shouldForwardToSubclass($methodName)
{
$myClass = get_class($this);
if (__CLASS__ != $myClass)
{
$classObject = new ReflectionClass($myClass);
$methodObject = $classObject->getMethod('__call');
$declaringClassName = $methodObject->getDeclaringClass()->getName();
return $myClass == $declaringClassName;
}
else
{
return false;
}
}
}
class ChildClass1 extends ParentClass {
public function __call( $method, $params ) {
if( ! parent::__call( $method, $params ) )
{
echo "Child handle!\n";
}
}
}
class ChildClass2 extends ParentClass {
}
later doing:
$c = new ChildClass1();
$c->one();
$c->foo();
$c = new ChildClass2();
$c->foo();
would yield:
Parent
Child handle!
PHP Fatal error: Uncaught exception 'Exception' with message 'No method' in /home/andres/workspace/Playground/test.php:18
Stack trace:
#0 /home/andres/workspace/Playground/test.php(58): ParentClass->__call('foo', Array)
#1 /home/andres/workspace/Playground/test.php(58): ChildClass2->foo()
#2 {main}
HTH
I'm encountering a tricky problem with Inheritance and the hierarchy of Exceptions offered by the Standard PHP Library (SPL).
I'm currently building a helper library in PHP for REST-based APIs. These APIs can return their own error messages in the form of JSON objects, and these objects include information beyond the properties offered by a PHP Exception. Here's a quick example:
{"error":{"time":"2011-11-11T16:11:56.230-05:00","message":"error message","internalCode":10}}
Occasionally, "message" includes internal structure that could benefit from additional parsing. I like the idea of throwing a particular subclass of Exception, like so:
$error = $json->error;
throw new UnexpectedValueException($error->message, $error-internalCode);
Which later can be selectively caught:
catch (UnexpectedValueException $e)
{
...
}
And now we arrive at my dilemma: I'd like to extend the SPL Exception objects so that they can have a "time" attribute, and also perform the extra parsing of "message." However, I'd like to extend them at their level as opposed to creating an extension of the base Exception class, so that the ability to selectively catch exceptions is preserved. Lastly, I'd like to avoid creating thirteen different child classes (the number of exception types defined in the SPL), if at all possible.
Ideally, I could begin with a parent customException object:
class customException
{
public $time;
public $message;
public $internalCode;
public function __construct($time, $message, $internalCode)
{
$this->time = $time;
$this->message = $message;
$this->internalCode = $internalCode;
}
public function parseMessage()
{
// Do some parsing of message
return $parsedMessage;
}
}
Then, I'd have a Factory Class that would be able to be invoked like so:
class ExceptionFactory
{
static public function createException(Exception $e, $exceptionParent)
{
$json = json_decode($e->message);
return new customException($json->time, $json->message, $json->internalCode) extends $exceptionParent; // Won't work, but hopefully you get the idea
}
}
After reading php dynamic class inheritance, I can probably get there by using eval(), but that just feels wrong to me. If I have to write the thirteen child classes, then I'll find myself wanting to use multiple inheritance for the desired parent class $exceptionParent and customException. How would you recommend I solve this dilemma? Thank you in advance for your ideas!
Having something like:
class MyException extends \Exception {
const EXCEPTION_TYPE_FOO = 1;
const EXCEPTION_TYPE_BAR = 2;
const EXCEPTION_TYPE_JSON_MESSAGE = 3;
$protected $_data = array();
$protected $_exceptionType = null;
public function __construct( $type = null ) {
if( null !== $type )
$this->_exceptionType = $type;
}
public function __get( $name ) {
if( isset($this->_data[$name]) ) {
if( $name == 'message' ) {
switch( $this->_exceptionType ) {
case MyException::EXCEPTION_TYPE_JSON_MESSAGE:
return json_decode($this->_data[$name]);
// other exception types
default:
return $this->_data[$name];
}
}
return $this->_data[$name];
}
return null;
}
public function __set( $name, $value ) {
$this->_data[$name] = $value;
}
}
So now you could have:
$e = new MyException(MyException::EXCEPTION_TYPE_JSON_MESSAGE);
$e->time = time();
$e->code = '404';
$e->message = json_encode(array('testing'));
And when you catch it
catch( MyException $e ) {
print_r( gettype($e->message) );
}
Should return array.
I haven't tested the code, I just wrote it but you get the idea.
One common solution is to use "marker interfaces" to indicate "their level"
interface MyExceptionLevel extends ParentExceptionLevel {}
class MyException extends Exception implements MyExceptionLevel{}
try {
// code
} catch (MyException $e) {}
// or
try {
// code
} catch (MyExceptionLevel $e) {}
I recommend not to use too much magic, especially in such a sensible point like error/exception handling.
Is there a way to throw exceptions from an SPL Autoloader in PHP in case it fails? It doesn't seem to work under PHP 5.2.11.
class SPLAutoLoader{
public static function autoloadDomain($className) {
if(file_exists('test/'.$className.'.class.php')){
require_once('test/'.$className.'.class.php');
return true;
}
throw new Exception('File not found');
}
} //end class
//start
spl_autoload_register( array('SPLAutoLoader', 'autoloadDomain') );
try{
$domain = new foobarDomain();
}catch(Exception $c){
echo 'File not found';
}
When the above code is called, there is no sign of an exception, instead I get a standard "Fatal error: Class 'foobarDomain' not found in bla". And the execution of the script terminates.
This is not a bug, it's a design decision:
Note: Exceptions thrown in __autoload function cannot be caught in the catch block and results in a fatal error.
The reason is that there may be more than one autoload handlers, in which case, you don't want the first handler to throw an Exception and bypass the second handler. You want your second handler to have a chance at autoloading its classes. If you use a library which makes use of the autoloading feature, you don't want it bypassing your autoload handler because they throw Exceptions inside their autoloader.
If you want to check whether or not you can instantiate a class, then use class_exists and pass true as the second argument (or leave it out, true is the default):
if (class_exists('foobarDomain', $autoload = true)) {
$domain = new foobarDomain();
} else {
echo 'Class not found';
}
According to the comments in the documentation for spl_autoload_register, it's possible to call another function from the autoloader, which in turn would throw the exception.
class SPLAutoLoader{
public static function autoloadDomain($className) {
if(file_exists('test/'.$className.'.class.php')){
require_once('test/'.$className.'.class.php');
return true;
}
self::throwFileNotFoundException();
}
public static function throwFileNotFoundException()
{
throw new Exception('File not found');
}
} //end class
//start
spl_autoload_register( array('SPLAutoLoader', 'autoloadDomain') );
try{
$domain = new foobarDomain();
}catch(Exception $c){
echo 'File not found';
}
Here's a full-fledged factory object which demonstrates auto-loading, namespaces support, callables from non-static instances (with variable paths), handling of loading errors and custom exceptions.
abstract class AbstractFactory implements \ArrayAccess
{
protected $manifest;
function __construct($manifest)
{
$this->manifest = $manifest;
}
abstract function produce($name);
public function offsetExists($offset)
{
return isset($this->manifest[$offset]);
}
public function offsetGet($offset)
{
return $this->produce($offset);
}
//implement stubs for other ArrayAccess funcs
}
abstract class SimpleFactory extends AbstractFactory {
protected $description;
protected $path;
protected $namespace;
function __construct($manifest, $path, $namespace = "jj\\") {
parent::__construct($manifest);
$this->path = $path;
$this->namespace = $namespace;
if (! spl_autoload_register(array($this, 'autoload'), false)) //throws exceptions on its own, but we want a custom one
throw new \RuntimeException(get_class($this)." failed to register autoload.");
}
function __destruct()
{
spl_autoload_unregister(array($this, 'autoload'));
}
public function autoload($class_name) {
$file = str_replace($this->namespace, '', $class_name);
$filename = $this->path.$file.'.php';
if (file_exists($filename))
try {
require $filename; //TODO add global set_error_handler and try clause to catch parse errors
} catch (Exception $e) {} //autoload exceptions are not passed by design, nothing to do
}
function produce($name) {
if (isset($this->manifest[$name])) {
$class = $this->namespace.$this->manifest[$name];
if (class_exists($class, $autoload = true)) {
return new $class();
} else throw new \jj\SystemConfigurationException('Factory '.get_class($this)." was unable to produce a new class {$class}", 'SYSTEM_ERROR', $this);
//an example of a custom exception with a string code and data container
} else throw new LogicException("Unknown {$this->description} {$name}.");
}
function __toString() //description function if custom exception class wants a string explanation for its container
{
return $this->description." factory ".get_class($this)."(path={$this->path}, namespace={$this->namespace}, map: ".json_encode($this->manifest).")";
}
}
and finally an example:
namespace jj;
require_once('lib/AbstractFactory.php');
require_once('lib/CurrenciesProvider.php'); //base abstract class for all banking objects that are created
class CurrencyProviders extends SimpleFactory
{
function __construct()
{
$manifest = array(
'Germany' => 'GermanBankCurrencies',
'Switzerland' => 'SwissBankCurrencies'
);
parent::__construct($manifest, __DIR__.'/CurrencyProviders/', //you have total control over relative or absolute paths here
'banks\');
$this->description = 'currency provider country name';
}
}
now do
$currencies_cache = (new \jj\CurrencyProviders())['Germany'];
or
$currencies_cache = (new \jj\CurrencyProviders())['Ukraine'];
LogicException("Unknown currency provider country name Ukraine")
If there is no SwissCurrencies.php file in /CurrencyProviders/,
\jj\SystemConfigurationException('Factory jj\CurrencyProviders was unable to produce a new class banks\SwissCurrencies. Debug data: currency provider country name factory jj\CurrencyProviders(path=/var/www/hosted/site/.../CurrencyProviders/, namespace=banks\, map: {"Germany": "GermanBankCurrencies", "Switzerland":"SwissBankCurrencies"}')
With enough effort this factory can be extended to catch parse errors (How to catch error of require() or include() in PHP?) and pass arguments to constructors.