I'm trying to create a class that enforces its type much like the PHP SplString class.
In this class you can create a string (example from php.net)
$string = new SplString("Testing");
try {
$string = array();
} catch (UnexpectedValueException $uve) {
echo $uve->getMessage() . PHP_EOL;
}
var_dump($string);
echo $string; // Outputs "Testing"
I know PHP is loosely typed however they have got this functionality in SplTypes so I was hoping there might be some way of mimicking this behavior.
I would be expecting to implement it in this kind of fashion below.
public function __assign($value)
{
if($value instanceof MyClass)
{
return true;
}
return false;
}
I'd like to be able to introduce this behavior in my own classes, but cant find the relevant magic method for assignments.
Is this even possible? Or is this a language feature that can't be controlled?
Related
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.
Suppose I have the following PHP code:
class Foo {
function getBar() {
return 1;
}
}
function check( Foo $foo ) {
if ( $foo->getBar() == 1 ) {
// here could be more code ...
return 'Oh no, there was an error in class' .
get_class( $foo ) . ', method ' .
'getBar';
}
}
The last string in check bothers me because if Foo::bar gets renamed by a refactoring tool, the error message will be wrong. Is there any way to get around this without using a string somewhere?
You can use __METHOD__ to get the name of the current method.
But to get reference to other method that would allow you some kind of automatic refactoring - no, it's not possible in php.
Can be done by using method_exists()
class Foo {
function getBar() {
return 1;
}
}
function check( Foo $foo , $method = 'getBar') {
if (!method_exists($foo, $method) ) {
// here could be more code ...
return 'Oh no, there was an error in class' .
get_class( $foo ) . ', method ' .
$method;
}
}
It is not possible in PHP per se, but you can implement such a feature. One possible implementation would work as follows: somewhere the file path, class name, method name and some kind of a description of where and what should match what. Your new feature whenever triggered would check the given files, check whether some values changed, fix whatever needs to be fixed and log a report about the task. It would not be simple to implement something like this, but, important to note is that there is a solution.
i am wondering is it possible to restrict php variable to accept only certain type of variable it is defined to accept.
e-g if we see in C#
public int variable = 20;
public string variable2 = "Hello World";
so what would it be like in php if i want to restrict type for variable.
public int $variable = 20;
so that if i try to assign string to integer variable i get the error.
public int $varaible = "Hello World"; //As type is integer, it should throw error.
is there such thing defining types in PHP for variables before assigning values to it??
TL;DR Not directly, no. PHP is not strictly-typed. There are, however, a few workarounds that may work for you in the context of function parameters or properties of classes.
Long answer: PHP is not a strictly-typed language, but loosely-typed. You can give a variable any value you want, regardless of how it was initialized. So, you can't simply type something like int $myVar and expect $myVar = "foo"; to throw an error. But PHP does offer a few handy features to get you to the same end when dealing with function parameters or properties of a class.
Option 1: Type hints
You can use a "type hint" for function parameters:
class SomeClass
{
/* code here */
}
function foo(SomeClass $data)
{
/* code here */
}
foo() will only accept parameters of type SomeClass. Passing it, say, an int will throw a fatal error. This doesn't work in PHP < 7 if the parameters are intended to be base types, like int, string, etc., so you can't do function foo(int $data) {...}. That said, there are a few libraries out there that attempt to force it to work at the expense of a little speed. Also, PHP 7 adds a lot of support for this kind of thing, as does the Hack language based on PHP.
Pros:
Easy
Intuitive
Cons:
Only works for program-defined classes
Unavailable for base types
Option 2: Getters and Setters
You can also use getters and setters, like so:
class SomeClass
{
private $foo = 0;
function setFoo($val = 0)
{
// force it to be an int
if (is_integer($val) {
$this->foo = $val;
} else {
// throw an error, raise an exception, or otherwise respond
}
}
}
Pros:
Relatively easy
Relatively intuitive
Cons:
Only works in program-defined classes
Unavailable for base types
Requires lots of code
Option 3: Magic Methods
This method is my favorite, but also the most complicated. Use the __set() magic method to deal with class properties.
class MyClass {
private $type = 0; // we will force this to be an int
private $string = ''; // we will force this to be a string
private $arr = array(); // we will force this to be an array
private $percent = 0; // we will force this to be a float in the range 0..100
function __set($name, $value) {
switch ($name) {
case "type":
$valid = is_integer($value);
break;
case "string":
$valid = is_string($value);
break;
case "arr":
$valid = is_array($value);
break;
case "percent":
$valid = is_float($value) && $value >= 0 && $value <= 100;
break;
default:
$valid = true; // allow all other attempts to set values (or make this false to deny them)
}
if ($valid) {
$this->{$name} = $value;
// just for demonstration
echo "pass: Set \$this->$name = ";
var_dump($value);
} else {
// throw an error, raise an exception, or otherwise respond
// just for demonstration
echo "FAIL: Cannot set \$this->$name = ";
var_dump($value);
}
}
}
$myObject = new MyClass();
$myObject->type = 1; // okay
$myObject->type = "123"; // fail
$myObject->string = 1; // fail
$myObject->string = "123"; // okay
$myObject->arr = 1; // fail
$myObject->arr = "123"; // fail
$myObject->arr = array("123"); // okay
$myObject->percent = 25.6; // okay
$myObject->percent = "123"; // fail
$myObject->percent = array("123"); // fail
$myObject->percent = 123456; // fail
Pros:
Relatively easy
Intuitive
Extremely powerful: one setter to rule them all
Cons:
Only works in program-defined classes
Unavailable for base types
Requires lots of switching or if/else logic
Can cause problems with IDEs not auto-completing property types correctly
Here's a demo of this approach.
Closing Thoughts
Finally, if you're using an IDE like PHPStorm, don't forget about PHPDoc type hints:
/* #var integer */
$foo = 0; // will result in warnings if the IDE is configured properly and you try to do something like substr($foo, 1, 4);
And if you really want to go hard core, you can do strong typing using Hack, at the expense of making your code less portable and less compatible (for now) with major IDEs.
Of course, none of these is a substitute for explicitly validating user input and thoroughly testing the application's response to unexpected input types.
No. PHP is not a strictly typed language. You can however use type hints in functions and methods.
If class or interface is specified as type hint then all its children or implementations are allowed too.
Type hints can not be used with scalar types such as int or string. Resources and Traits are not allowed either.
The Scalar types being:
string
bool
int
float
Examples:
function (array $theArr) {
// body
}
class X {
public function __construct(SomeOtherClass $arg) {
// body
}
public function setFoo(Foo $foo) {
}
}
See the manual for more specifics: http://php.net/manual/en/language.oop5.typehinting.php
You have to made it by your own hands, example :
function setInt(&$var, $value) {
if(!is_integer($value) {
throw new Exception("Integer wanted " . gettype($value) . " received");
}
$var = $value;
}
Take a look at the following illustration:
// Trims input, fixes spaces and encodes bad glyphs. Also works with arrays.
function prepare_param($param)
{
$retval = "";
function prc($param)
{
$r = split(" ", trim($param));
foreach($r as $i => $e)
$r[$i] = urlencode($e);
return join("+", $r);
}
// If input is an array
if(is_array($param))
{
$retval = array();
foreach($param as $e)
$retval[] = prc($e);
}
// If input is a string
else if(is_string($param))
{
return prc($param);
}
else throw new Exception("Invalid input! Expected String or Array.");
}
Obviously the function prc will now be declared globally, even though declared inside a function. Is there a way to follow this principle, creating a tiny function/macro inside another function as not to litter the global scope? The alternative would be to make a class with a private function, which seems like overkill for my use.
Any help appreciated
You probably want closures, which are anonymous functions.
If you have PHP 5.3, enter anonymous functions:
$prc = function($param)
{
$r = split(" ", trim($param));
foreach($r as $i => $e)
$r[$i] = urlencode($e);
return join("+", $r);
};
if(is_array($param))
{
$retval = array();
foreach($param as $e)
$retval[] = $prc($e);
}
else if(is_string($param))
{
return $prc($param);
}
In this case, $prc only lives in the scope of your prepare_param() function.
If you have access to >=PHP 5.3, you can use anonymous functions, and if not, you can use create_function.
If you don't have PHP 5.3, you can use the create_function function.
There are two ways to do so. The closures/anonymous functions are possible from PHP 5.3, and the oldschool way would be to use create_function() - which is quite fugly.
However in your case, you don't want either. There is no benefit in creating or recreating the function. You just need it once, as it does not depend on any initialization state. The idiom you should use is called "dererred definition" and possible in PHP with:
if (!function_exists("prc")) {
function prc($param) {
...
}
}
You should name it with its parent function as prefix however (e.g. prepare__prc) to avoid clashes and to signalize its internal use.
Oh, and btw it could also be simplified compacted into:
$param = join("+", array_map("urlencode", split(" ", trim($param))));
anonymous functions might be what you are looking for
<?php
$greet = function($name)
{
printf("Hello %s\r\n", $name);
};
$greet('World');
$greet('PHP');
?>
If you don't use php 5.3 please be aware of the fact that the memory allocated by the "create_function()" function isn't released until the php process finishes. So if you create a lot of functions you might be running into issues.
I tried to ask this before, and messed up the question, so I'll try again. Is it possible to make an object return false by default when put in an if statement? What I want:
$dog = new DogObject();
if($dog)
{
return "This is bad;"
}
else
{
return "Excellent! $dog was false!"
}
Is there a way this is possible? It's not completely necessary, but would save me some lines of code. thanks!
No, PHP has no support for operator overloading. Maybe they'll add it in a future version.
Use the instanceof keyword.
For example
$result = Users->insertNewUser();
if($result instanceof MyErrorClass){
(CHECK WHAT WENT WRONG AND SAY WHY)
} else {
//Go on about our business because everything worked.
}
Info is here.
Use this? Not a real neat solution, but does what you want:
<?php
class Foo
{
private $valid = false;
public function Bar ( )
{
// Do stuff
}
public function __toString ( )
{
return ( $this -> valid ) ? '1' : '0';
}
}
?>
Zero is considered false, one is considered true by PHP
I was attempting to do this myself and found a solution that appears to work.
In response to the others who were trying to answer the question by telling the asker to use a different solution, I will also try to explain the reason for the question. Neither the original poster or I want to use an exception, because the point is not to use exception handling features and put that burden on any code we use this class in. The point, at least for me, was to be able to use this class seamlessly in other PHP code that may be written in a non-object-oriented or non-exception-based style. Many built-in PHP functions are written in such a way that a result of false for unsuccessful processes is desirable. At the same time, we might want to be able to handle this object in a special way in our own code.
For example, we might want to do something like:
if ( !($goodObject = ObjectFactory::getObject($objectType)) ) {
// if $objectType was not something ObjectFactory could handle, it
// might return a Special Case object such as FalseObject below
// (see Patterns of Enterprise Application Architecture)
// in order to indicate something went wrong.
// (Because it is easy to do it this way.)
//
// FalseObject could have methods for displaying error information.
}
Here's a very simple implementation.
class FalseObject {
public function __toString() {
// return an empty string that in PHP evaluates to false
return '';
}
}
$false = new FalseObject();
if ( $false ) {
print $false . ' is false.';
} else {
print $false . ' is true.';
}
print '<br />';
if ( !$false ) {
print $false . ' is really true.';
} else {
print $false . ' is really false.';
}
// I am printing $false just to make sure nothing unexpected is happening.
The output is:
is false.
is really false.
I've tested this and it works even if you have some declared variables inside the class, such as:
class FalseObject {
const flag = true;
public $message = 'a message';
public function __toString() {
return '';
}
}
A slightly more interesting implementation might be:
class FalseException extends Exception {
final public function __toString() {
return '';
}
}
class CustomException extends FalseException { }
$false = new CustomException('Something went wrong.');
Using the same test code as before, $false evaluates to false.
I recently had to do something similar, using the null object pattern. Unfortunately, the null object was returning true and the variable in question was sometimes an actual null value (from the function's default parameter). The best way I came up with was if((string)$var) { although this wouldn't work for empty arrays.
Putting something in "an if statement" is simply evaluating the variable there as a boolean.
In your example, $dog would need to be always false for that to work. There is no way to tell when your variable is about to be evaluated in a boolean expression.
What is your ultimate purpose here? What lines of code are you trying to save?
I'm not sure about the object itself. Possible. You could try something like, add a public property to the DogObject class and then have that set by default to false. Such as.
class DogObject
{
var $isValid = false;
public function IsValid()
{
return $isValid;
}
}
And then when you would instantiate it, it would be false by default.
$dog = new DogObject();
if($dog->IsValid())
{
return "This is bad;"
}
else
{
return "Excellent! $dog was false!"
}
Just a thought.
If I understand what your asking, I think you want to do this:
if (!$dog){
return "$dog was false";
}
The ! means not. SO you could read that, "If not dog, or if dog is NOT true"
Under what conditions do you want if($dog) to evaluate to false? You can't do what you've literally asked for, but perhaps the conditioned could be replaced by something that does what you want.
class UserController
{
public function newuserAction()
{
$userModel = new UserModel();
if ($userModel->insertUser()) {
// Success!
} else {
die($userModel->getError());
}
}
}
Or
class UserController
{
public function newuserAction()
{
$userModel = new UserModel();
try {
$userModel->insertUser()
}
catch (Exception $e) {
die($e);
}
}
}
There are a million ways to handle errors. It all depends on the complexity of the error and the amount of recovery options.
How about using an Implicit Cast Operator like the following C# ?
like so:
class DogObject
{
public static implicit operator bool(DogObject a)
{
return false;
}
}
Then you can go...
var dog = new DogObject();
if(!dog)
{
Console.WriteLine("dog was false");
}