say I have a block of repeated code (I'll keep the example simple, although consider that the actual repeated functionality is considerably longer, but goes through the same logic, but with different error codes and messages)
public function firstThing($working) {
if(!$working){
$message = "The first things not working";
throw new Throwable\FirstThingNotWorking($message);
}
return true;
}
public function secondThing($working) {
if(!$working){
$message = "number two not working";
throw new Throwable\NumTwoThingNotWorking($message);
}
return true;
}
public function thirdThing($working) {
if(!$working){
$message = "number three not working";
throw new Throwable\NumeroTresThingNotWorking($message);
}
return true;
}
I'm bugged because it seems I could change these to call a single function, which could then be called by any of the above functions with 1 line, something like
private function checkIfWorking($working, $message, $throwableClass){
if(!$working) {
throw new $throwableClass($message);
}
return true;
}
I'd rather not instantiate the throwable object unless it is used, but I'm unsure how to pass just the throwable class in such a way that it can be instantiated by the function.
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.
How can I see if an exception is currently in flight, i.e. the stack is unwinding?
In the example below how would you implement isExceptionInFlight()?
<?php
class Destroyer
{
function __destruct() {
if (isExceptionInFlight()) {
echo 'failure';
} else {
echo 'success';
}
}
}
function isExceptionInFlight() {
// ?????
}
function createAndThrow()
{
$var = new Destroyer;
throw new \Exception;
}
createAndThrow();
The purpose of this would be to implement D's scope statement, which is available as a library in multiple other languages. This allows you to get rid of nested try-catch blocks, which in turn makes it easier to do transactions with rollbacks correctly.
Addendum1:
I've looked around in the Zend PHP Engine and executor_globals.exception seems to be what I'm looking for (https://github.com/php/php-src/blob/master/Zend/zend_globals.h). However this value is always nullptr when I inspect it during __destruct(). Any idea where I should look next?
Addendum2:
Inspecting executor_globals.opline_before_exception has led to some progress. However it is not reset to nullptr when the exception has been caught.
Addendum3:
I've found the following code (line 135)
/* Make sure that destructors are protected from previously thrown exceptions.
* For example, if an exception was thrown in a function and when the function's
* local variable destruction results in a destructor being called.
*/
old_exception = NULL;
if (EG(exception)) {
if (EG(exception) == object) {
zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
} else {
old_exception = EG(exception);
EG(exception) = NULL;
}
}
zend_call_method_with_0_params(&obj, object->ce, &destructor, ZEND_DESTRUCTOR_FUNC_NAME, NULL);
if (old_exception) {
if (EG(exception)) {
zend_exception_set_previous(EG(exception), old_exception);
} else {
EG(exception) = old_exception;
}
}
This seems to actively PREVENT me from doing what I want, and explains why executor_globals.exception is always nullptr.
Although I don't recommend, I have implemented it in the past. My approach was (simply put) like this:
Implement custom Exception class
class MyException extends Exception {
public static $exceptionThrown = false;
public function __construct($your parameters) {
self::$exceptionThrown = true;
}
}
Now, every exception should be your own exception implementation instead of default Exception.
class Destroyer {
public function __destruct() {
if(MyException::exceptionThrown() {
Database::rollback();
} else {
Database::commit();
}
}
}
I've been trying to optimise a backend system to make it just load classes as and when they're required, since there's no point loading every class when they're not required. I know we have the spl_autoload_register in PHP. At present I have my registry using __get and __set to access and set variables lazily, along with a loading function to add new classes to the registry as an object.
class registry {
public function __set($index, $value){
if(!isset($this->$index)){
$this->vars[$index] = $value;
} else {
die("The variable ".__CLASS__."->".$index." is already in use and cannot be redefined.");
}
}
public function __get($index){
if(isset($this->vars[$index])){
return $this->vars[$index];
} else if(isset($this->$index)){
return $this->$index;
} else {
$debug_backtrace = debug_backtrace();
$callee = next($debug_backtrace);
die("The variable \$".__CLASS__."->".$index." does not exist!");
}
}
public function load($class){
if(isset($class) && !empty($class) && file_exists('/_class/'.$class.'.class.php')){
include_once('/_class/'.$class.'.class.php');
$this->$class = new $class();
if(is_object($this->$class)){
} else {
die('Not found!');
}
}
} else if(isset($class) && !empty($class)){
die('The class `'.$class.'` does not exist!');
}
}
}
The above works great, at the moment, so all I need to do is the following:
$registry = new registry();
$registry->load('router');
$registry->load('mysql');
$registry->load('settings');
//etc
However, to make it use everything as and when needed, say settings doesn't appear on every page, I thought I could just change the __get section of the class as follows:
public function __get($index){
if(isset($this->vars[$index])){
return $this->vars[$index];
} else if(isset($this->$index)){
return $this->$index;
} else if($this->load($index)){ // additional line
return $this->$index; // additional line
} else {
$debug_backtrace = debug_backtrace();
$callee = next($debug_backtrace);
die("The variable \$".__CLASS__."->".$index." does not exist!");
}
}
However, all I keep getting now is Notice: Undefined property: registry::$settings in ... which is the if(is_object($this->$class)){ line, which I do not understand as it works normally via the function route as outlined previously, so an object success gets created through the current method, but not in the new one. Even as basic as the below, the same error appears (and hi there appears in the browser`):
class settings {
function __construct(){
echo 'hi there!';
}
}
I know I'm probably missing something very small, but an extra pair of eyes may help :o)
Seems I forgot to return the class via the load function :o(
I have a class in php that works with the chainning method, but the problem is that I want to chain the methods in some order.
class Chain {
public function foo () {
return $this;
}
public function bar () {
return $this;
}
public function some () {
return $this;
}
}
So, if I use this class, then I can chain this methods in 9 different ways (all the possible combinations of 3 elements)
But what happen if I determine that the method some always must to be chained after foo or bar and not in other way?
$chain = new Chain();
$chain->foo->bar(); //works; i.e: the method some is optional
$chain->foo()->bar()->some(); //works
$chain->bar()->foo()->some(); //works
$chain->some()->bar()->foo(); //throws an exception
I think that I can do this setting boolean values, something like: when the method foo or bar are called, then I set the value to some var to true, and when the developer calls the some function, if that var is false, then throws an exception, otherwise is allowed to continue.
But I need something more elegant, such as pattern or a built-in solution.
There is another way to do it?
The very rough example I imagine will still have some lines of code in each method
<?php
class Chain {
private $_register = array();
public function foo () {
$this->register(__METHOD__);
return $this;
}
public function bar () {
$this->register(__METHOD__);
return $this;
}
public function some () {;
$this->verify('foo'); // foo() should be called before some();
$this->register(__METHOD__);
echo 'it\'s ok';
return $this;
}
public function verify($method) {
if(array_key_exists($method, $this->_register) && $this->_register[$method] == true) {
return true;
}
else {
throw new Exception('Some exception');
}
}
public function register($method) {
$method = str_replace(__CLASS__.'::', '', $method);
$this->_register[$method] = true;
}
}
What do we do here - we have a register() and verify() methods. (they can be helpers, but for the current purpose I added them in the class.
Each method should have before it's returning value a register to itself. Calling $this->register(__METHOD__) from foo() will add in the private array 'foo' => true.
The verify() method checks if foo exist as array key and if its value is true. If it is - the script will continue. Otherwise - throws exception.
In this case:
$chain = new Chain();
$chain->bar()->some()->foo(); //throws an exception
Fatal error: Uncaught exception 'Exception' with message 'Some
exception' in ...
$chain = new Chain();
$chain->foo()->some()->foo(); // ok
it's ok
The problem here is that we establish a "convention". You need to pass __METHOD__ to the register function so after it replace the classname it will add only the method name in the array. So later, in the function where you need to verify if one or more functions are called before this, you need to use the method name as string i.e. $this->verify('foo');
Ofcourse you can play different scenarios without stripping and testing with strpos() or adding () after the methodname for easier recognition if you are verifying a method or smth else.
But at least it will save you from making for each method, different variable to fill i.e.
function foo() {
$this->_foo = true;
return $this;
}
function bar() {
$this->_bar = true;
return $this;
}
Forcing the caller to stick to a certain order of calls just as an end to itself is hardly useful at all. Supposedly what you're really interested in is to make sure the state of the object is valid when you call some() and throw an exception if it's not. In that case, yes, you would check certain indicators of your object's state and throw an exception when this state does not fulfil the requirements that some() may be called. As a concrete example:
$api = new SomeAPI;
$api->setUserID($id);
$api->setSecretKey($secret);
$api->call('something');
Here call() would check that the user id and access key has been set, otherwise it can't do its job. Whether these calls are chained or not is irrelevant and just a syntactic detail.
Alternatively, you could return certain objects of other (sub) classes from your methods which physically make it impossible to call certain methods on them if certain conditions haven't been met:
public function bar() {
if ($this->foo) {
return new SubFoo($this->foo);
} else {
return new SubBar;
}
}
This may be overly complicated though.
I'm attempting to chain multiple setter methods together. I'd like to know if anyone knows of a clean way to parse through multiple errors, preferably using a try-catch block and handling using the default Exception class.
As the code stands now, all I receive is the first Exception object (from foo) that is caught, so I can only display one error to the user.
Any pointers or conventions would be appreciated!
class Test {
function setName($name) {
if (empty($name))
throw new Exception("Name is empty.");
return $this;
}
function setDescription($description) {
if (empty($description))
throw new Exception("Description.");
return $this;
}
try {
$object->setName("foo")
->setDescription("bar");
} catch(Exception $e) {
}
You can catch different Exceptions but only 1 at a time. Exceptions are not just warnings or notices, an exception means that a function could not get the expected results and the program can't simply go on. It is not ment to be used to tell the user he didn't fill in a specific field.
Use other techniques for that.
You can not throw more than one exception by it's definition (similar to why you can not return result from function more than once).
In your case - you can store some structure with results, to which you will write errors of operations in your class. Then, if it's not empty, throw an exception with gathered content.
When an Exception is thrown, the code stop and the Exception will bubble until a catch instruction will handle it.
So when you do:
$object->setName("foo")
->setDescription("bar");
If the call of setName throws an exception, you second setter won't be called.
You need to handle errors by yourself in your case:
class Test {
private $errors = array();
function setName($name) {
if (empty($name))
$this->errors[] = "Name is empty.";
return $this;
}
function setDescription($description) {
if (empty($description))
$this->errors[] = "Description.";
return $this;
}
You might want to look into error handling and exceptions: Exceptions are for exceptional situations, these look like situations that you can come across a lot: If there is an unexpected situation, you throw an exception and stop whatever you are doing. As the state is unknown, you cannot continue doing more work because you are in this exceptional situation.
If you want to direct flow in the case of an error, you should write some sort of error-handler not based on exceptions so you continue work, but report the error back to the user.
There is a lot to read about this, you want to look for resources on using "exceptions as flow control", and why it is a bad idea.
The point of an Exception is that something has happened from which you can not recover. Thus it halts the execution and bubbles up until it's at the top (Error: Uncaught Exception) or until it is caught and handled by a try/catch.
If you absolutely want multiple errors, you could keep track of that state inside of your class:
abstract class Statefull // abstract because it makes no sense instantiating this
{
protected $isValid = true;
protected $errors = array();
protected function invalidate($field, $error) {
$this->isValid = false;
$this->errors[$field] = $error;
}
public function isValid() { return $this->isValid; }
public function getErrors() { return $this->errors; }
}
This example class allows for one error per field and can be extended as follows.
class Test extends Statefull
{
protected $name;
protected $description;
function setName($name) {
if (empty($name)) this->invalidate('name', 'Name is empty');
else $this->name = $name;
return $this;
}
function setDescription($description) {
if (empty($description)) this->invalidate('description', 'Description is empty');
else $this->description = $description;
return $this;
}
}
And can be used like this:
$test = new Test();
$test->setName('')->setDescription('');
if (!$test->isValid()) {
var_dump($test->getErrors());
die();
}
If you have PHP 5.4, you could also solve this problem with traits:
trait Statefull
{
protected $isValid = true;
protected $errors = array();
protected function invalidate($field, $error) {
$this->isValid = false;
$this->errors[$field] = $error;
}
public function isValid() { return $this->isValid; }
public function getErrors() { return $this->errors; }
}
The implementation will then be like this, with the advantage of not needing to extend a class (if you are allready extending your models from a different base class):
class Test
{
use Statefull;
protected $name;
protected $description;
function setName($name) {
if (empty($name)) this->invalidate('name', 'Name is empty');
else $this->name = $name;
return $this;
}
function setDescription($description) {
if (empty($description)) this->invalidate('description', 'Description is empty');
else $this->description = $description;
return $this;
}
}
The usage is the same as before. If you do this, be aware of the restrictions properties in traits bring:
If a trait defines a property then a class can not define a property with the same name, otherwise an error is issued. It is an E_STRICT if the class definition is compatible (same visibility and initial value) or fatal error otherwise.