My current way:
class A {
public function function_b($myint) {
if (!is_numeric($myint)) return false;
// code ...
}
}
I would like to abandon the function is_numeric() like this:
public function function_b(Integer $myint) {
// code ...
}
It works with arrays like this:
public function function_c(Array $arr) {
// only executes following code if $arr is an array / instance of Array!
}
Note: the function has to return false if the value isn't a number (int)! I don't want to cast it.
How would you short my current code? Thanks in advance!
You can't force strict types in function prototypes in PHP inherently, because it's not a strictly typed language. PHP is a weakly typed language and trying to go against the grain will only hurt you in many situations. Also, is_numeric does not guarantee that your value is of type int (for what it's worth).
What you can do is analyze your need for why you think this approach is necessary in the first place and decide on how to best implement this without creating potential for bugs.
For example, take the following scenario where what your method expects is an ID for a database query.
class MyClass {
public function getUser($id) {
if (!is_int($id)) {
throw new Exception("Invalid argument supplied. Expecting (int), but argument is of type (" . gettype($id) . ").");
}
// Otherwise continue
$db = new PDO($dsn);
$stmt = $db->prepare("SELECT username FROM users WHERE user_id = ?");
$stmt->execute(array($id));
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
}
$MyObject = new MyClass;
$result = $MyObject->getUser($_POST['id']);
/* The problem here is $_POST will always be of type string. */
What this should tell you is that it makes no sense to force type checking here since PHP will have done the right thing for you had you just let it alone.
The question you need to be asking yourself is not "How do I force strict typing?", but rather "Why would I need to force strict typing at all?".
You should look into typecasting:
http://php.net/manual/en/language.types.type-juggling.php#language.types.typecasting
Just use (int) when accessing the value to typecast it to an integer.
You could just typecast it:
public function function_b($myint) {
$myint = (int) $myint;
}
Or better yet add a public setter to class A which will do it for you every time you set the value:
class A
{
public function setMyInt($myInt)
{
$this->myInt = (int) $myInt;
}
}
-- Update (based on comment) --
class A
{
public function doSomethingWithAnArray(array $array)
{
....
}
}
Notice the keyword array in the signature of the doSomethingWithAnArray method, now if you don't pass an array to this function PHP will throw a fatal error and cease code execution. This is known as typehinting, and can be applied to objects as well.
function needsInteger($int) {
if (((int) $int) != $int) return false;
// ...
}
The advantage here is that you can still accept loosely typed parameters, but the non-strict equality check against the cast value will yield an acceptable result.
Related
I have read the section of returning references in PHP by putting the ampersand in both the function definition and variable assignment. But, I have yet to find examples of "returning references" in php code that does not relate to Object Oriented Programming. Can anyone provide a use for this and an example?
Let me start, from a very simplified example,
class Test {
//Public intentionally
//Because we are going to access it directly later
//in order to see if it's changed
public $property = 'test';
/**
* Look carefully at getPropReference() title
* we have an ampersand there, that is we're indicating
* that we're returning a reference to the class property
*
* #return string A reference to $property
*/
public function &getPropReference()
{
return $this->property;
}
}
$test = new Test();
//IMPORTANT!! Assign to $_foo a reference, not a copy!
//Otherwise, it does not make sense at all
$_foo =& $test->getPropReference();
//Now when you change a $_foo the property of an $test object would be changed as well
$_foo = "another string";
// As you can see the public property of the class
// has been changed as well
var_dump($test->property); // Outputs: string(14) "another string"
$_foo = "yet another string";
var_dump($test->property); //Outputs "yet another string"
Update: This answer relates to passing by reference, not returning by reference. Retained for it's information value.
Read this:
http://php.net/manual/en/language.references.pass.php
Then take a look at this example:
<?php
function AddTimestamp(&$mytimes)
{
$mytimes[] = time();
}
$times = array();
AddTimestamp($times);
AddTimestamp($times);
AddTimestamp($times);
// Result is an array with 3 timestamps.
Can this be better implemented using object oriented techniques? Perhaps, but from time to time there is a need/reason to modify an existing value-based data structure or variable.
Consider this:
function ValidateString(&$input, &$ErrorList)
{
$input = trim($input);
if(strlen($input) < 1 || strlen($input) > 10)
{
$ErrorList[] = 'Input must be between 1 and 10 characters.';
return False;
}
}
$Err = array();
$Name = ' Jason ';
ValidateString($Name, $Err);
// At this point, $Name is trimmed. If there was an error, $Err has the message.
So, depending on your needs, there are still times to pass by reference in PHP. Objects are always passed by reference, so anytime you encapsulate your data in an object, it automatically becomes a reference.
I write some class to work with string like in C#.
Here it is:
class String {
private $origin_string = null;
private $result_string = null;
function __construct($string)
{
$this->origin_string = $string;
$this->result_string = $this->origin_string;
}
public function Trim()
{
$this->result_string = Trim($this->result_string);
return $this->result_string;
}
public function StartWith($string)
{
return (substr($this->result_string, 0, strlen($string)) === $string);
}
public function EndWith($string)
{
$endlen = strlen($string);
$strlen = strlen($this->result_string);
return (substr($this->result_string, $strlen - $endlen, $endlen) === $string);
}
public function Contains($string) {
return (strpos($this->result_string, $string) !== false);
}
public function Replace($search, $string) {
$this->result_string = str_replace($search, $string, $this->result_string);
return $this->result_string;
}
public function __invoke($string) {
$this->origin_string = $string;
$this->result_string = $this->origin_string;
return $this;
}
public function __toString()
{
return $this->result_string;
}
public static function Override($string)
{
return new self($string);
}
}
In use:
$s = new String("My custom string");
if ($s->StartWith("My"))
$s->Replace("custom", "super");
print $s; // "My super string"
To correct print my text from object i use magic method __toString().
Question:
Is there a method, the inverse __toString?
That is so we can write:
$s = "new text";
And the line is assigned to variables within the object.
($s - an existing object "String" in the example above.)
An analogue of the method __set, only in relation to the object, not the variable inside it.
While using the __invoke, but it's not quite what I want.
No.
$s = "new text"; assigns the (native PHP) string "new text" to the variable $s. It overwrites whatever $s was before. It does not call any methods on $s if $s is an object.
You'd have to alter the core behavior of PHP to achieve something like that. You'll always have to explicitly call a method on your String object.
The short answer to your direct question is "No, there isn't any way to do that in PHP".
Strings are a primitive data type in PHP, and it doesn't do operator overloading or any other other features you'd need to enable this kind of thing.
But also, because they're a primitive data type, there's no real need to encapsulate them in an object structure like this. PHP's OO capabilities have come a long way in recent versions, but at its heart it still isn't a fully OO language.
In fact, I'd say that what you're doing is counter productive. You're wrapping the concept of a string up into a class that has significantly less functionality than basic PHP. You're writing a whole stack of code in order to do stuff in one line of code that can already be done in one line of code, and you're limiting the ability to do a whole lot more.
For example, you've got Contains() and StartsWith() methods, but they don't deal with regular expressions in any way.
And how are you going to deal with concatenation? And what about embedding variables into strings?
PHP has a lot of string handling functionality (in fact, string handling it's one of its strengths), which your class isn't going to be able to replicate.
I recommend working with the language you've been given, not trying to force it to conform to your syntax ideals.
No, you can't assign directly a value to your object. PHP does not allow operator overloading and this style assignment. You must use the contructor, the invoke or any setter method to assign a new value to your string.
You can write something like this:
$s = 'myclass';
$o = new $s();
or, if you want to 'compile' the new keyword you could do:
$s = '$x = new myclass();';
eval($s);
hope this helps.
I have a few "setter" methods across classes, and for convenience I've added an optional parameter $previous, which takes an argument by reference and populates it with the existing value before replacing it with the new one. For example:
public function set_value($key, $value, &$previous = null)
{
$previous = $this->get_value($key);
$this->_values[$key] = $value;
return $this;
}
This works fine; however in some circumstances, the corresponding "getter" method is a bit process intensive, and running it unconditionally is a waste. I figured I could test:
if(null !== $previous)
{
$previous = $this->get_value($key);
}
This doesn't work though, as often the variable passed as the argument for $previous hasn't been previously defined in it's scope, and defaults to null anyway. The only solution I've hacked out is:
public function set_value($key, $value, &$previous = null)
{
$args = func_get_args();
if(isset($args[2])
{
$previous = $this->get_value($key);
}
$this->_values[$key] = $value;
return $this;
}
Or, to one-line it:
if(array_key_exists(2, func_get_args()))
{
// ...
}
I don't like the method body being reliant on the argument indices (when it seems it should be unnecessary) Is there a cleaner way to achieve what I'm after here?
I've tried:
if(isset($previous)){}
if(!empty($previous)){}
if(null !== $previous){}
Neither work.
Possible solutions thus far:
if(func_num_args() == $num_params){}
if(array_key_exists($param_index, func_get_args())){}
// 5.4
if(isset(func_get_args()[$param_index])){}
// 5.4
if(func_num_args() == (new \ReflectionMethod(__CLASS__, __FUNCTION__))
->getNumberOfParameters()){}
#DaveRandom -- So, something in the area of:
define('_NOPARAM', '_NOPARAM' . hash('sha4096', microtime()));
function foo($bar = _NOPARAM)
{
// ...
}
#hoppa -- Use case:
$obj->set_something('some_key', $some_value, $previous) // set
->do_something_that_uses_some_key()
->set_something('some_key', $previous) // and reset
->do_something_that_uses_some_key()
-> ...
Instead of:
$previous = $obj->get_something('some_key'); // get
$obj->set_something('some_key', $some_value) // set
->do_something_that_uses_some_key();
->set_something($previous) // and reset
->do_something_that_uses_some_key();
-> ...
possibly not how you wanted to solve your problem (testing somehow optional arguments), but this is how I would implement it:
public function set_value($key, $value)
{
$this->_values[$key] = $value;
return $this;
}
public function set_get_value($key, $value, &$previous)
{
$previous = $this->get_value($key);
$this->_values[$key] = $value;
return $this;
}
Use case example:
$obj->set_get_something('some_key', $some_value, $previous) // set AND get
->do_something_that_uses_some_key()
->set_something('some_key', $previous) // and reset
->do_something_that_uses_some_key()
-> ...
Why use another function?
This solution has a few advantages:
the name is more explicit, less confusion for other coders
no hidden side effects
solves your problem with (undefined) variables already having a value
no overhead of calling func_num_args, or some other "meta" function
EDIT: typo in code.
EDIT 2: removed default value of &$previous set_get_value() function (thanks to draevor)
Extracted from the comments / discussion above:
In order to check whether the argument was passed you have 2 options - check the value of the argument against a value (as you've done with null) or check the number of arguments.
If you go with the first option, there's no value that cannot be passed from outside the function, so there will always be a chance for false positives (the same thing that's happening now with null). DaveRandom's example with a random string should be enough for most cases though, but I see it as overkill.
I think the second option is the most clean (fast, readable, etc). As a small improvement over what you've already done with func_get_args, I'd use func_num_args - this way you'll be checking the number of passed arguments, not the argument indices.
First of all, I apologize that this question is so vague. I can't remember what this is called, or how they work, so it's very difficult to start searching or formulate a good title.
I have two questions wrapped into one:
First:
How are objects converted to other types internally? What is this called?
Example:
$Obj{
$value = 1;
$other = 2;
$more = 3;
}
$myObj = (string)$Obj;
print $myObj; // prints "1, 2, 3" or something like that
Second:
Can this method be used in math? Is there some override function that recognizes when an Object is being used in math?
Example:
$Obj{
$value = 1;
$other = 2;
$more = 3;
}
$result = 4 / $Obj;
print $result; // prints ".66666667" or something similar (sum of all properties)
Update:
I think it might have something to do with serialize(), but I know I've heard of a case where this is done "automatically" without having to call serialize() and it's done in a way that doesn't actually serialize the whole object, it just converts it to a useable value, like my above examples.
Final:
Thanks for #trey for being right about it being casting and to #webbiedave for pointing me to the magic method __toString.
It is casting as you can define the magic method __toString to allow the object to be cast to a string as desired, which will then allow PHP to cast it to an int or float in math.
Take the following example:
class A
{
public $value = 1;
public $other = 2;
public $more = 3;
public function __toString()
{
return (string)($this->value + $this->other + $this->more);
}
}
$obj = new A();
echo 4 / (string)$obj; // outputs 0.66666666666667
It's called type casting when you change an object to a different data type, as for the second part, I'm not entirely sure I understand you, are you trying to type cast during a math function?
it sounds like this may be more along the lines of what you're looking for:
class User
{
public $first_name='John';
public $last_name='Smith';
public function __toString()
{
return "User [first='$this->first_name', last='$this->last_name']";
}
}
$user=new User;
print '<span>'.$user.'</span>';
but I'm unable to find documentation about how to make this work when the object is converted to an interger... I'll update if I do
I'm sorry for asking this question, but I'm not good in php (beginner).
Could you please explain what $arg means in this piece of code? (it's from one of drupal modules)
function node_add_review_load($arg) {
global $user;
$add_review = FALSE;
$current_node = node_load($arg);
$type =$current_node->type;
$axes_count = db_result(db_query("SELECT COUNT(*) FROM {nodereview_axes} WHERE node_type='%s'", $type));
if (variable_get('nodereview_use_' . $type, 0) && $axes_count) {
$add_review = db_result(db_query("SELECT n.nid FROM {node} n INNER JOIN {nodereview} nr ON n.nid=nr.nid WHERE uid=%d AND reviewed_nid=%d", $user->uid, $arg));
}
return $add_review ? FALSE : $arg;
}
Thank you.
http://nl.php.net/manual/en/functions.arguments.php
When a programmer uses node_add_review_load() he can pass the argument which can be used in the function.
The function returns another value if the argument $arg is different.
So the programmer can do this:
node_add_review_load("my argument");
//and the php automatically does:
$arg = "my argument";
//before executing the rest of the function.
In general, arg is short for "argument," as in "an argument to a function." It's a generic, and thus unhelpful, name. If you'd just given the method signature (function node_add_review_load($arg)) we'd have no idea.
Fortunately, with the complete function body, we can deduce its purpose: it is the node_id. This function loads the node identified by $arg and then tries to find a corresponding row that's loaded, and that the code then tries to find a corresponding review for the current user. If successful, the function will return that same node_id (i.e., $arg); otherwise it will return FALSE.
It's an argument.
Example,
// function
function sum($arg1, $arg2)
{
return $arg1+$arg2;
}
// prints 4
echo sum(2,2);
You don't have to call it $arg for it to be valid. For example,
function sum($sillyWilly, $foxyFox)
{
return $sillyWilly+$foxyFox;
}
And it would work the same. You should give arguments useful names. In this case, the argument $arg is bad programming practice because someone like you would look at it and get confused by what it means exactly. So in cases where you make functions, be sure to use useful names so you'll remember.