Chained PHP controls - php

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;
}
}
}

Related

PHP - nagate callback result

Is there any way to negate result of callback?
$this->myInjectedService->doSomething([$this, 'myCallback']);
I need to nagate result of myCallback method. I know I can do it by nested function, but is there any more clean way (exclamation mark)?
$this->myInjectedService->doSomething(function() {
return !$this->myCallback();
});
No, there is no direct way to do it like that.
What you could do, would be to create a callback wrapper which negates the callback you want to call. Every time you want to negate a callback you can use that wrapper instead of the callback itself:
<?php
class SomeService
{
public function doSomething(callable $callback)
{
return call_user_func($callback);
}
}
final class CallbackWrapper
{
public static function call(callable $callback, bool $negate = false): callable
{
if ($negate) {
return function() use ($callback) {
return !call_user_func($callback);
};
}
return $callback;
}
}
class Test
{
public function __construct()
{
$this->someService = new SomeService();
}
public function doSomething()
{
// Scenario 1: use direct callback
$result = $this->someService->doSomething([$this, 'thisIsACallback']);
var_dump($result);
// Scenario 2: use wrapper without negating
$result = $this->someService->doSomething(
call_user_func_array('CallbackWrapper::call', [[$this, 'thisIsACallback']])
);
var_dump($result);
// Scenario 3: use wrapper with negation
$result = $this->someService->doSomething(
call_user_func_array('CallbackWrapper::call', [[$this, 'thisIsACallback'], true])
);
var_dump($result);
}
public function thisIsACallback(): bool
{
return true;
}
}
$test = new Test();
$test->doSomething();
The results would be:
// Scenario 1
bool(true)
// Scenario 2
bool(true)
// Scenario 3
bool(false)
Please note that this is the simplest approach I could think of, but if you need more complex callbacks to be called then you might need to update the wrapper.
PS: Even if it's possible, I don't recommend using this approach, as using a nested function, as you already did, is much more clear and easier to read/understand.

Is having the entire function body inside an if statement considered bad practice?

I often find myself writing functions which look like:
public function foo($param): void
{
if($param) {
//do something
}
}
with no code executing if $param evals to false.
This looks like a bad practice, are there any guidelines/patterns on how to avoid this kind of coding?
Add access modifiers to function
Class properties must be defined as public, private, or protected. If declared using var, the property will be defined as public.
public function foo($param=null)
{
/* Condition while not empty then proceed */
if(!empty($param)) {
//do something
} else {
return false;
}
}
It depends on what you want to do with the function.
But my opinion is your condition should be outside of the function, for example
function foo() {
// doing something
}
and then
if ($params) {
foo();
}
it would look more customizable
You can improve the function like this:
function foo($param='defaultValue')
{
if($param)
{
//do something
return true; // or something else
}
return false; // by default return false
}
By doing this, if $param will be evaluated as false, the return false will be used.
Also, in case your function is inside a class, it's best practice to specify an access modifier (public, protected, private) for that function.
Personally I'd change this code to:
function foo($param)
{
$param
&& (function () {
// Do code here.
})();
}
but then if I wanted to call the inner function elsewhere, and keep everything clean, I'd probably refactor it into this:
function foo($param)
{
$param
&& bar();
}
function bar()
{
// Do code here
}
Then I'd adjust the naming of foo to make it clear that it's a wrapper over bar and rename bar so that it sounds like it's missing the wrapper, maybe something like action_only or action_without_foo
Then I'd also write it up in the dochints for each function, in OOP land this would be a private method, but that doesn't enforce true privacy either see: What is the point of OOP visibility in PHP when Closures and Reflections are available?, in fact the closure inside the function, in the first example, is more "private" on a technical level
If a function is an all-or-nothing condition, I have the else condition first - this way, if the $param resolves as false the function quits immediately:
public function foo($param): void
{
if(!$param) {
return;
}
//do something
}

How to filter all returned values from methods in class

I inherited this PHP project. I've got one class which contains maybe 20 different methods, most of which return values. I'd like to filter every value returned by methods in this class. Is there a simple and elegant way to do this, or must I wrap every value in a filter method before it is returned?
The class looks like this:
class db_ops
{
// ...
public function get_var($query) {
// ...
return $values;
}
public function get_name($query) {
// ...
return $name;
}
// ...
}
I've used a sort of wrapper class to do something like this before. Here's a generic example, just to show how it works. If you like this idea you should be able implement your own along these lines.
class FilteredExample
{
private $example;
function __construct($example)
{
$this->example = $example;
}
public function __call($name, $arguments)
{
if (method_exists($this->example, $name)) {
$result = call_user_func_array([$this->example, $name], $arguments);
if (is_string($result)) {
return "$result filtered";
} elseif (is_array($result)) {
return array_map(function($item){ return "$item filtered"; }, $result);
} else {
return $result;
}
} else {
throw new BadMethodCallException("Method '$name' not found", 1);
}
}
}
You inject the object whose methods you want to filter in the constructor, and then use the __call() magic method so that you can call all of the original object's methods. With method_exists, you can validate any called method and throw an exception if it doesn't exist, just like you'd get if you called it on the original object. If it does exist, then you can call it with call_user_func_array, filter the result, and return it.
I added a little section to show that you can handle different result types, or just return the unmodified result if it's not a certain type. You obviously wouldn't need to do this if all your original object's methods return the same type.
Here is an example of how you could use it based on the example class from your question:
$dbOps = new db_ops;
$filteredDbOps = new FilteredExample($dbOps);
$result = $filteredDbOps->get_var('something');

PHP Syntax not allowed

I would like to know why the syntax below cannot work with PHP. Anyone with an insight perhaps it does not make any sense or something.
($this->getState($state))->getEvent('eventOpen')->attach($observeCallback);
Basically the getState($state) method returns an object that within itself has both getEvent($eventName) and attach($observerCallbackToAttach).
However, this throws an error and doesn't work.
Am I breaking the rules for thinking this is a valid syntax?
Please note that ->attach($observeCallback) runs on the result of ->getEvent('eventOpen') -- not the result of $this->getState($state).
Check what the getEvent() method returns. For this to work it needs return $this;.
Or else, clarify you code as such:
$tmp = $this->getState($state);
$tmp->getEvent('eventOpen');
$tmp->attach($observeCallback);
Or alternatively, move the attach() method to the class used in the object returned by getEvent()
if the getState method returns an object which has 2 methods getEvent and attach then to make this work you have to return the same object with getEvent / attach. When you do getEvent()->attach() you are using the return value of getEvent not getState.
What you want to do to make this work is to return $this in your methods:
class Something
{
function setState($state)
{
if ($state != '') {
$this->state = $state;
}
return $this;
}
function setUberState($uberState)
{
if ($uberState != '') {
$this->uberState = $uberState;
}
return $this;
}
}
Using the above class you should be able to do:
$something = new Something();
$object = $something->setState('state')->setUberState('uberstate');

How can I achieve something like $app->error()->encode() in PHP?

I want to include this function in a class. This function will essentially json_encode the output of the previous function (I hope that makes sense).
I want to do something like this:
<?php
$app = new App;
// $app->error(); // Should return ['some error', 'some other error']
echo $app->error()->encode(); // Should return {'errors': ['some error', 'some other error']}.
Also what's the correct term for such function? I've been searching but couldn't find anything as I didn't really know what I was looking for.
Thanks!
Edit
I think you got that wrong. It's my mistake I didn't mention it before.
That's just an example. Just like in frameworks like Slim, where you can do something like:
$response->getBody()->write('Something');
I want to do something similar to that. Not just that. I want to learn how it's done.
Here is some boilerplate code you could use. The idea is that you should make the error method return an object of yet another class. That object should then in turn have the encode method.
In your example, you want $app->error() to return an array. For it to behave as an array, we can extend the ArrayObject class.
Secondly, you want that same $app->error() to expose another method encode. So you define that method in that same class mentioned above:
// Extend ArrayObject to make objects behave as arrays
class ErrorMsg extends ArrayObject {
// Add method to return JSON string
public function encode() {
return json_encode(array("errors" => $this->getArrayCopy()));
}
}
class App {
private $error;
public function doSomething() {
// For demo sake, just set an error:
$this->error = ["An error occurred in doSomething", "No further info"];
}
public function error() {
// This is the key: instantiate and return another object
return new ErrorMsg($this->error);
}
}
$app = new App;
// Set an error condition in $app
$app->doSomething();
// Loop over error array
foreach ($app->error() as $index => $error) {
// Display the error
echo "Error $index is: $error\n";
}
// Display the JSON encoding of the same.
echo $app->error()->encode() . "\n";
Output:
Error 0 is: An error occurred in doSomething
Error 1 is: No further info
{"errors":["An error occurred in doSomething","No further info"]}
See it run on eval.in
General idea to chain method calls
In general, when you want your objects to support chained -> notation, you must make sure to define each method as returning yet another object with its own methods. Then those methods can again return objects, with again exposed methods, etc. And so you can chain-call on and on.
So if you want to be able to write:
$a = new A();
$result = $a->b()->c()->d();
...then your code would look something like this:
class D {
// ...
}
class C {
private $d;
public function C() { // constructor
$this->d = new D();
}
public function d() {
return $this->d;
}
}
class B {
private $c;
public function B() { // constructor
$this->c = new C();
}
public function c() {
return $this->c;
}
}
class A {
private $b;
public function A() { // constructor
$this->b = new B();
}
public function b() {
return $this->b;
}
}
Of course, this is just the structure, and you'd pass some specific data to either the constructors and/or methods. But the main idea is that you never return simple types (String, Number, ...), but always objects.

Categories