I have a PHP class that can return FALSE.
For some weird reason, I am unable to detect the FALSE return.
I can detect any other return value without any trouble.
Dumbed down version....
error_reporting(E_ALL);
ini_set("display_errors", 1);
$run = new myClass();//class returns false
$response = $run->myMethod();
I've tested with the following...
!$response
!isset($response)
empty($response)
$response == false
$response === false
I'm unable to get any type of reaction for any of the conditions I'm familiar with.
If I set the return to 1 or ANYTHING other than false, $response will contain whatever the class returns.
It's not like this is my first day. (it's my 2nd).
I use return false; frequently and have never had any trouble until today.
Certainly I can fix this by returning something like 'wtf' and test for that condition, but I'd really like to figure out what I'm missing here.
Thanks for any ideas.
When you make a instance of your class it will always your return a object not the return of the construct.
<?php
class SomeClass
{
// construct function that will always return false
public function __construct()
{
return false;
}
}
class TestClass extends PHPUnit_Framework_TestCase
{
public function testReturn()
{
// check if this code returns false
$this->assertFalse(new SomeClass);
}
public function testAssert()
{
// Checks if the object return is actually of SomeClass
$this->assertInstanceOf('SomeClass', new SomeClass);
}
public function testReturnOfConstruct()
{
//checks the return of the construct
$object = new SomeClass;
$this->assertFalse($object->__construct());
}
}
Return of phpunit
phpunit --debug test3.php
PHPUnit 3.7.28 by Sebastian Bergmann.
Starting test 'TestClass::testReturn'.
F
Starting test 'TestClass::testAssert'.
.
Starting test 'TestClass::testReturnOfConstruct'.
.
Time: 7 ms, Memory: 5.25Mb
There was 1 failure:
1) TestClass::testReturn
Failed asserting that SomeClass Object () is false.
/var/www/test3.php:14
FAILURES!
Tests: 3, Assertions: 3, Failures: 1.
Are you trying to return false from within public function __construct() or public function MyClass()*? This won't work as the new MyClass() call will either trigger a PHP error, or return an instance of MyClass, which is never going to evaluate to false because it is an instance of an object and thus not false.
If your class does not need to be instantiated to function, you can create a static method to perform the logic you are trying to run like so:
<?php
class MyClass
{
const COLOUR_BLUE = 'blue';
public static function isSkyBlue()
{
if (isset($_POST['sky']) && $_POST['sky'] == self::COLOUR_BLUE) {
return true;
} else {
return false;
}
}
}
// Call doThings() statically to get its value
$response = MyClass::isSkyBlue();
if ($response === false) {
// Your logic goes here
}
Alternatively, if you are passing things to the constructor before it performs its logic, you can do the following:
<?php
class MyClass
{
const COLOUR_BLUE = 'blue';
protected $otherObject = null;
public function __construct($otherObject)
{
$this->otherObject = $otherObject;
}
public function isSkyBlue()
{
if ($this->otherObject->sky == self::COLOUR_BLUE) {
return true;
} else {
return false;
}
}
}
// Call doThings() statically to get its value
$response = new MyClass($otherObject)
if ($response->isSkyBlue() === false) {
// Your logic goes here
}
* Note that your constructor should always be called __construct() as using the class name for the constructor has been deprecated since PHP 5.
If you are using a constructor, it wont return anything...it is not a normal function
Related
I am converting an old project that uses all functions to use a class. Not all installed instances will be updated so I have to have the code work with the function or the class but I can't get it to work. Here is an outline of the code I am using:
function GetStatus($id) {
return true;
}
class myclass (
public function GetStatus($id) {
return true;
}
}
if (class_exists('myclass')) {
$fcn = $myclass->GetStatus;
} else {
$fcn = GetStatus;
}
echo 'result = ' . $fcn($user_id);
So the code checks if the class exists and uses that method, else it uses the function. The code runs without errors but when the class method is used the return is not shown. That is, the results are
result = 1 //when function is used
result = //when method is used
Is this possible? If it matters, the minimum php version is 7.1.
To make a callable from an instance and method, you use an array containing the instance and the method name.
$fcn = [$myclass, 'GetStatus'];
Checking for a myclass existance when you already have the myclass instance stored in a $myclass variable makes no sense. Maybe you should check for a method existance?
if (method_exists($myclass, 'GetStatus')) {
$fcn = function($id) use ($myclass){
return $myclass->GetStatus($id);
};
} else {
$fcn = function($id){
return GetStatus($id);
};
}
echo 'result = ' . $fcn($user_id);
This is my Foo class
<?php
class FooClass
{
public function doFoo($total)
{
$gst = (($this->getTaxRate() / 100) * $total);
return $gst;
}
public function getTaxRate()
{
return 5;
}
}
And this is my test.
public function fooTest()
{
$mockedObj = $this->createMock(FooClass::class);
$mockedObj->expects($this->exactly(1))
->method('getTaxRate')
->will($this->returnValue(10));
$this->assertEquals(10, $mockedObj->doFoo(100));
}
Output:
Failed asserting that null matches expected 10.
I can see getTaxRate() function is not being mocked .It is also returning null instead of 5(default value). I am expecting getTaxRate() inside doFoo() to return the mock value i.e 10 else if I do not mock a getTaxRate, it should return 5.
Anything that I am missing here?
Consider the example classes (apologies for it being so convoluted, but it's as slim as possible):
class RecordLookup
{
private $records = [
13 => 'foo',
42 => 'bar',
];
function __construct($id)
{
$this->record = $this->records[$id];
}
public function getRecord()
{
return $this->record;
}
}
class RecordPage
{
public function run(RecordLookup $id)
{
return "Record is " . $id->getRecord();
}
}
class App
{
function __construct(RecordPage $page, $id)
{
$this->page = $page;
$this->record_lookup = new RecordLookup($id);
}
public function runPage()
{
return $this->page->run($this->record_lookup);
}
}
In which I want to test App whilst mocking RecordPage:
class AppTest extends \PHPUnit_Framework_TestCase
{
function testAppRunPage()
{
$mock_page = \Mockery::mock('RecordPage');
$mock_page
->shouldReceive('run')
->with(new RecordLookup(42))
->andReturn('bar');
$app = new App($mock_page, 42);
$this->assertEquals('Record is bar', $app->runPage());
}
}
Note: the expected object argument ->with(new RecordLookup(42)).
I would expect this to pass however Mockery returns throws No matching handler found for Mockery_0_RecordPage::run(object(RecordLookup)). Either the method was unexpected or its arguments matched no expected argument list for this method.
I'm assuming this is because a strict comparison is used for the arguments expected through with() and new RecordLookup(42) === new RecordLookup(42) evaluates as false. Note new RecordLookup(42) == new RecordLookup(42) evaluates as true so if there was someway of relaxing the comparison it would fix my problem.
Is there a proper way to handle expected instance arguments in Mockery? Maybe I'm using it incorrectly?
You can tell mockery that a RecordLookup instance (any) should be received:
$mock_page
->shouldReceive('run')
->with(\Mockery::type('RecordLookup'))
->andReturn('bar');
But this will match any instance of RecordLookup. If you need to dig inside the object and check if it's value is 42, then you can employ a custom validator:
$mock_page
->shouldReceive('run')
->with(\Mockery::on(function($argument) {
return
$argument instanceof RecordLookup &&
'bar' === $argument->getRecord()
;
}))
->andReturn('bar');
There are more options, well explained in the docs.
As an alternative, the documentation proposes to use equalTo($object).
For example:
$userRepository->shouldReceive('create')
->once()
->with(\Hamcrest\Core\IsEqual::equalTo(
new User(0, "Test", "Dummy", "fakelogin")));
I'm making a form validation class and it works like this currently.
$validator->setVar($_POST['Username'])
->standardFilter(array('XSS', 'SQL_INJECTION'))
->customRegex()
->replace('This', 'With this')
->getResult();
While it works perfectly when chained like this, I can't archieve the following result.
$validator->setVar($_POST['Username'])
->isValidEmail()
->isValidPhoneNumber()
->isSet()
->isNull()
->getResult()
For example, script returns the following values
->isValidEmail() (true)
->isValidPhoneNumber() (true)
->isSet() (false)
Basically, I'm going to make an array, fill it with true/false depending on the result of each function, and I'll look for a specific value in array (a false). If it exists, the class will return false regardless of the rest of the chains. (or I can just override variables, not important here.)
However, I want $validator to stop chaining once it gets a false from a function. Let's say it received a false from isSet(). It shouldn't execute isNull() and getResult() since we already have a failed check.
How can I archieve this in PHP?
TL;DR:
var_dump($validator->setVar('Test message')->isInteger()->setTrue());
//false //true
Output: false, because once isInteger() failed, rest of the chain isn't executed.
How can I archieve this in PHP?
Nothing like good source code to learn from. I would suggest exploring the Zend Framework's Validation classes. It provides the same chaining functionality you describe.
...More source code check isValid() specifically.
Try something like this
class FooBar
{
private $SomethingWrong = false;
function Bar()
{
if( $this->SomethingWrong )
throw new Exception('SomeThing is wrong');
return $this;
}
function Foo()
{
return $this
}
}
$foobar = new FooBar();
$foobar->Bar()
->Foo();
The Foo() part will not be executed, because of the exception in the Bar().
Of course, there are some variations. If you do not want a exception, but a silent non-execute, you could try this:
class FooBar
{
private $SomethingWrong = false;
function Bar()
{
$this->SomethingWrong = true;
return $this;
}
function Foo()
{
if( !$this->SomethingWrong ) {
// do my stuff
}
return $this
}
}
The only way to do this, in any language, is to throw an exception. You can't return the validator object (which is necessary for chaining) and also return true or false, all while having the chaining work. That said, I am not advocating the use of exceptions in this manner. I am in complete agreement with vascowhite's comments below.
Rather than have it stop in the middle of the chain, why not consider the isSet, isNull, etc. methods as instructions to tell the validator what to check. Then have a validate method called at the end of the chain. The validate method can perform the validation based on the validator state (as set by the other methods). And that validate method can also return a true or a false, or a custom state object, with the result of the validation.
Instead of return a value, you can throw a custom exception, which abort the code execution.
Add an try-catch block to the code, handle your exception and everything works fine.
EDIT:
What you also can do is a little bit magic and not really to be recommed. But nice to know, this is possible in php, so better use Exceptions
class PassThroughValidator extends ...
{
private $val;
public function __construct($result)
{
$this->val = $result;
}
public function __call($name, $arguments)
{
return $this;
}
public function getResult()
{
return $this->val;
}
}
class EmailValidator extends ...
{
function isMail()
{
if (...) {
// do something here
return $this;
}
// set Result to false or something similar
return new PassThroughValidator($this->getResult());
}
}
Considering that the value returned in each step of the chain is an object, you can not have one of the chained methods return true/false. it must always return an object instance. So I guess what you would need to do is add some property on the object to indicate that validations should not be done, and if the property is set, just ignore the validation attempt and return the object as is.
So perhaps something like this in simplified form, showing only one such validation:
class validator {
protected $ignore_validations = false;
protected $value = null;
protected $is_null;
public function isNull () {
if(true === $this->ignore_validations) {
return $this;
} else if(is_null($this->value)) {
$this->is_null = true;
$this->ignore_validations = true;
return $this;
} else {
$this->is_null = false;
return $this;
}
}
}
Ok i have a problem, sorry if i cant explaint it clear but the code speaks for its self.
i have a class which generates objects from a given class name;
Say we say the class is Modules:
public function name($name)
{
$this->includeModule($name);
try
{
$module = new ReflectionClass($name);
$instance = $module->isInstantiable() ? $module->newInstance() : "Err";
$this->addDelegate($instance);
}
catch(Exception $e)
{
Modules::Name("Logger")->log($e->getMessage());
}
return $this;
}
The AddDelegate Method:
protected function addDelegate($delegate)
{
$this->aDelegates[] = $delegate;
}
The __call Method
public function __call($methodName, $parameters)
{
$delegated = false;
foreach ($this->aDelegates as $delegate)
{
if(class_exists(get_class($delegate)))
{
if(method_exists($delegate,$methodName))
{
$method = new ReflectionMethod(get_class($delegate), $methodName);
$function = array($delegate, $methodName);
return call_user_func_array($function, $parameters);
}
}
}
The __get Method
public function __get($property)
{
foreach($this->aDelegates as $delegate)
{
if ($delegate->$property !== false)
{
return $delegate->$property;
}
}
}
All this works fine expect the function __set
public function __set($property,$value)
{
//print_r($this->aDelegates);
foreach($this->aDelegates as $k=>$delegate)
{
//print_r($k);
//print_r($delegate);
if (property_exists($delegate, $property))
{
$delegate->$property = $value;
}
}
//$this->addDelegate($delegate);
print_r($this->aDelegates);
}
class tester
{
public function __set($name,$value)
{
self::$module->name(self::$name)->__set($name,$value);
}
}
Module::test("logger")->log("test"); // this logs, it works
echo Module::test("logger")->path; //prints /home/bla/test/ this is also correct
But i cant set any value to class log like this
Module::tester("logger")->path ="/home/bla/test/log/";
The path property of class logger is public so its not a problem of protected or private property access.
How can i solve this issue? I hope i could explain my problem clear.
EDIT:
A simple demonstration
Modules::Name("XML_Helper")->xmlVersion ="Hello"; // default is 333
$a = Modules::Name("XML_Helper")->xmlVersion; // now $a should contain "Hello"
echo $a; // prints 333
What i need is
Modules::Name("XML_Helper")->xmlVersion ="Hello"; // default is 333
$a = Modules::Name("XML_Helper")->xmlVersion; // now $a should contain "Hello"
echo $a; // prints Hello
I realise you already said that path is public, but it's still worth mentioning: If you're using PHP 5.3.0+, note this quirk of property_exists():
5.3.0 | This function checks the existence of a property independent of
accessibility
In other words, if you check if (property_exists($delegate, $property)), you have no guarantee you have access to $delegate->$property for writing (or reading, for that matter, but you are trying to write).
As for actual troubleshooting: You could try checking if your if (property_exists($delegate, $property)) statement actually executes. If it doesn't, check the case of $property.
Sidenote: It's fairly hard to read the code you posted up, which makes it a bit of a pain to troubleshoot. Could you edit your post and indent it properly?
The path property of class logger is public so its not a problem of
protected or private property access.
That's your problem. From the docs:
__set() is run when writing data to inaccessible properties.
That suggests that __set() is not called for public properties.