I am trying to get some errors returned in JSON format. So, I made a class level var:
public $errors = Array();
So, lower down in the script, different functions might return an error, and add their error to the $errors array. But, I have to use return; in some places to stop the script after an error occurs.
So, when I do that, how can I still run my last error function that will return all the gathered errors? How can I get around the issue of having to stop the script, but still wanting to return the errors for why I needed to stop the script?!
Really bare bones skeleton:
$errors = array();
function add_error($message, $die = false) {
global $errors;
$errors[] = $message;
if ($die) {
die(implode("\n", $errors));
}
}
If you are using PHP5+ your class can have a destructor method:
public function __destruct() {
die(var_dump($this->errors));
}
You can register a shutdown function.
Add the errors to the current $_SESSION
Add the latest errors to any kind of cache, XML or some storage
If the code 'stops':
// code occurs error
die(print_r($errors));
You can use a trick involving do{}.
do {
if(something) {
// add error
}
if(something_else) {
// add error
break;
}
if(something) {
// add error
}
}while(0);
// check/print errors
Notice break, you can use it to break out of the do scope at any time, after which you have the final error returning logic.
Or you could just what's inside do{} inside a function, and use return instead of break, which would be even better. Or yes, even better, a class with a destructor.
Related
I am just curious if it's possible to force parent method to return a value from within method called in that parent method? Let's say I have:
public function myApiEndpoint()
{
// I DO NOT want to to have return statement here
$this->validOrUnprocessable();
// some other code
//return value
return $someValue;
}
public function validOrUnprocessable()
{
if ($condition) {
... here goes the code that forces return statement on myApiEndpoint function without putting the word `return` in front of this call...
}
}
So in other words validOrUnprocessable method, when it needs to do so forces or tricks PHP into thinking that myApiEndpoint returns the value. I do not want to use return statement when validOrUnprocessable is called or any if conditions.
I do know other ways of doing what I want to do but I wanted to know if something like that is possible. I am not interested in any workarounds as I know very well how to implement what I need to achieve in many other ways. I just need to know if this what I described is possible to do exactly how I described it.
I did try to get there with reflections and other scope related things but so far no luck. Any ideas?
Just to add. I am doing this because I want to check how far I can push it. I am building a tool for myself and I want it to be as convenient and easy to use as possible.
If it's not possible I have another idea but that's a bit out of the scope of this post.
You should throw an exception.
public function validOrUnprocessable()
{
if ($condition) {
throw Exception('foo bar');
}
}
The code calling this method should be ready to catch an exception:
public function myApiEndpoint()
{
try {
// I DO NOT want to to have return statement here
$this->validOrUnprocessable();
// some other code
//this code will never be called because of exception thrown in validOrUnprocessable
return value;
} catch (Exception $e) {
//do something else
return -1; //you can return another value as example.
}
return $someValue;
}
I need to know if there is a better way to avoid Call to a member function xxxx() on null
currently I'm coding as follows but it is cumbersome.
if($event->getForm()
&& $event->getForm()->getParent()
&& $event->getForm()->getParent()->getParent()
&& $event->getForm()->getParent()->getParent()->getData()
&& $event->getForm()->getParent()->getParent()->getData()->getComponente()
){
$componente = $event->getForm()->getParent()->getParent()->getData()->getComponente();
$formModifier($event->getForm(), $componente, $defaultComponente);
}
In PHP 7 this is actually a catchable Error (if you're using hhvm it's a regular Exception):
try {
$componente = $event->getForm()->getParent()->getParent()->getData()->getComponente();
} catch (\Error $e) {
$componente = null;
}
if ($componente !== null) {
$formModifier($event->getForm(), $componente, $defaultComponente);
}
In PHP 5 there is a workaround using intermediate variables and the and keyword instead of &&:
if (
$f = $event->getForm() and
$p = $f->getParent() and
$p2 = $p->getParent() and
$d = $p2->getData() and
$componente = $d->getComponente()
) {
$formModifier($f, $componente, $defaultComponente);
}
If you use && instead of and you'll get "undefined variable" notices and this workaround won't work.
Working examples: https://3v4l.org/0S6ps
no there is no way, but at least you can do some performance improvement
$form = $event->getForm();
if(!$form){
//do error handling
return;
}
$parent = $form->getParent();
if(!$parent){
//do error handling
return;
}
$p_parent = $parent->getParent();
if(!$p_parent){
//do error handling
return;
}
$data = $p_parent->getData();
if(!$data){
//do error handling
return;
}
$component = $data->getComponente();
...
this way you call each function only once and you can do better error handling
I think this is a great example of a bad code. By having a code like this you're breaking several rules and making your life much harder than it should be.
Your code is rigid, fragile, hard to understand and maintain etc.
Simpler is ALWAYS better.
If you can't make your $xx->getComponent() a proper object easily accessible without such ugly nested relationship, you should at least encapsulate the method into something appropriate and use that instead, so if anything changes, you don't have to go full mental and change it all over the place.
This class seems strange in it's creation, but if you are not extracting these methods dynamically using __call(), you can use method_exists() in a loop inside a function, something similar to:
function getMethodChain($class,$arr = ['getForm','getParent','getParent','getData','getComponente'])
{
# First check the object is set
if(!is_object($class))
return false;
# Loop intended method chain
foreach($arr as $method) {
# Check if the method exists in the current class or passed already
$useClass = (!isset($classPass))? $class : $classPass;
# Check if the method exists in the current class
if(is_object($useClass) && method_exists($useClass,$method)) {
# Assign this class/method to use next in the loop
$classPass = $useClass->{$method}();
}
else
return false;
}
# Just send back
return (isset($classPass))? $classPass : false;
}
The use would be something like:
# This will either be the data you expect or false
$componente = getMethodChain($event);
So, as part of a project, I was considering building a flagging system. The idea behind this would be a cron job that runs daily to determine whether each of a series of flags still applied to a specific object (and if so, save that flag data for the object).
// code stub
$flags = $this->getFlags();
foreach($flags as $flag)
{
$className = 'Svc_Flags_'.$flag->flag_code;
if(class_exists($className, false)
{
(new $className())->setFlag();
}
}
And right now, in the dummy code for that class, I have a constructor that echos a simple text message, and the function setFlag() that echos a different text message.
<?php class Svc_Flags_Test extends Svc
{
public function __construct()
{
echo 'construct<br/>';
}
public function setFlag()
{
echo 'set flag<br/>';
}
}
Now, this doesn't work. By that, I mean that I am not seeing either echo.
However, if I do this:
// code stub
$flags = $this->getFlags();
foreach($flags as $flag)
{
$className = 'Svc_Flags_'.$flag->flag_code;
(new $className())->setFlag(); // This is the added line of code
if(class_exists($className, false)
{
(new $className())->setFlag();
}
}
I get the constructor echo, and the setFlag() echo TWICE.
Why is this happening? Now, I'm pretty sure I could just wrap part of this in a try/catch block to get past any errors if a class isn't there, but I'm curious as to why it doesn't seem to find the class unless I explicitly call it before the if statement.
I have a PHP file that can be include'd() in various places inside another page. I want to know whether it has been included inside a function. How can I do this? Thanks.
There's a function called debug_backtrace() that will return the current call stack as an array. It feels like a somewhat ugly solution but it'll probably work for most cases:
$allowedFunctions = array('include', 'include_once', 'require', 'require_once');
foreach (debug_backtrace() as $call) {
// ignore calls to include/require
if (isset($call['function']) && !in_array($call['function'], $allowedFunctions)) {
echo 'File has not been included in the top scope.';
exit;
}
}
You can set a variable in the included file and check for that variable in your functions:
include.php:
$included = true;
anotherfile.php:
function whatever() {
global $included;
if (isset($included)) {
// It has been included.
}
}
whatever();
You can check if the file is in the array returned by get_included_files(). (Note that list elements are full pathnames.) To see if inclusion occurred during a particular function call, check get_included_files before and after the function call.
I'm totally new with CodeCeption.
I want to do an action/assertion depending on another assertion result, like this:
if ($I->see('message')){
$I->click('button_close');
}
Is something like that possible? I tried, but doesn't work.
Probably the assertion result doesn't apply to IF, but is there any alternative?
Thanks in advance!
IMPORTANT UPDATE:
Finally Codeception now has the function performOn!!
http://codeception.com/docs/modules/WebDriver#performOn
I had this same issue. Although it's not ideal, you can do this:
try {
$I->see('message');
// Continue to do this if it's present
// ...
} catch (Exception $e) {
// Do this if it's not present.
// ...
}
In tests/_support/AcceptanceHelper.php add additional method
function seePageHasElement($element)
{
try {
$this->getModule('WebDriver')->_findElements($element);
} catch (\PHPUnit_Framework_AssertionFailedError $f) {
return false;
}
return true;
}
Then to test in your acceptance test use:
if ($I->seePageHasElement("input[name=address]")) {
$I->fillField("input[name=address]", "IM");
}
You can use a workaround like this or similar combinations:
$tmp = $I->grabTextFrom('SELECTOR');
if ($tmp == 'your text') {
$I->click('button_close');
}
ULTIMATE SOLUTION!
Finally Codeception now has the function performOn, wich does exactly what I asked for!!
[Version 2.2.9]
http://codeception.com/docs/modules/WebDriver#performOn
Answering my example:
$I->performOn('.message', ['click' => '#button_close'], 30);
Waits up to 30 seconds to see the element with class='message', then click the element with id='button_close'.
Codeception now has tryTo..., e.g, tryToSee() trytoClick(), etc., so there's no need for a Try/Catch block. I find it more readable than performOn().
You need to enable it with this in acceptance.suite.yml or codeception.yml:
# enable conditional $I actions like $I->tryToSee()
step_decorators:
- \Codeception\Step\TryTo
- \Codeception\Step\ConditionalAssertion`
You can click on something that may or may not be there with:
$I->tryToClick('#save_button`);
If there's no button, the code goes on with no error message. This could also be used to click on the node to expand a section of a tree before examining it, but only if that section is closed (there should always be a class that's only there when it's closed).
Another way to go is in an if statement. The tryTo... methods all return true on success and false on failure, so you can do this, which some might consider clearer than the above (no error will be thrown):
if ($I->tryToSee('some_locator')) {
$I->click('some_locator');
}
This form is also useful if there is a sequence of actions you want to perform based on a condition, the else is optional.
if ($I->tryToSee('some_locator')) {
$I->fillField('username', 'myname');
$I->fillfield('password', 'mypassword);
$I->click('Submit');
} else {
/* Do something else */
}
Release for my project comes on weeken
/**
* https://stackoverflow.com/questions/26183792/use-codeception-assertion-in-conditional-if-statement
* #param $element
* #return bool
* #throws \Codeception\Exception\ModuleException
*/
public function seePageHasElement($element)
{
$findElement = $this->getModule('WebDriver')->_findElements($element);
return count($findElement) > 0;
}
The reason why the assertion doesn't work with conditional statements is that Codeception first executes the assertion inside the IF brackets, and in case it is not true - it fails the test right away. My way of overcoming this is using the SOFT assertions TryTo which will be ignored by Codeception if fails: https://codeception.com/docs/08-Customization#Step-Decorators
if ($I->tryToSee('message')){
$I->click('button_close');
}```
100% working solution !! Thanx me later ;)
In tests/_support/AcceptanceHelper.php add additional method
public function seePageHasElement($element)
{
try {
$this->getModule('WebDriver')->_findElements($element);
} catch (\PHPUnit_Framework_AssertionFailedError $f) {
return false;
}
return true;
}
Then to test in your acceptance test use:
if ($I->seePageHasElement($element)) {
$I->fillField($element);
}
The 'seeElement' function of WebDriver doesnot work on this case so needs little modified function as i have used one '_findElements' .Please donot forget to build your actors after any changes done.