Php string is a value type? - php

Why php's string is a value type? It is copied all over the place every time the argument is passed to a function, every time the assignment is made, every concatenation causes string to be copied. My .NET experience tells me that it seems inefficient and forces me to use references almost everywhere. Consider the following alternatives:
Alternative 1
// This implementation hurts performance
class X {
public $str;
function __construct($str) { // string copied during argument pass
$this->$str = $str; // string copied here during assignment
}
}
Alternative 2
// This implementation hurts security
class Y {
public $str;
function __construct(&$str) {
$this->$str = &$str;
}
}
// because
$var = 'var';
$y = new Y($var);
$var[0] = 'Y';
echo $y->var; // shows 'Yar'
Alternative 3
// This implementation is a potential solution, the callee decides
// whether to pass the argument by reference or by value, but
// unfortunately it is considered 'deprecated'
class Z {
public $str;
function __construct($str) {
$this->$str = &$str;
}
}
// but
$var = 'var';
$z = new Z(&$var); // warning jumps out here
$var[0] = 'Z';
echo $y->var; // shows 'Zar'
The question: What pain should I choose Performance / Security / Deprecation

PHP handle's it's variables pretty reasonably. Internally, PHP uses a copy-on-modification system.
That is to say that those values will be assigned by reference until one of them is changed, in which case it will get a new slot in memory for the new value.

Passing vars by reference is going to hurt performance.
Your example #1 is the best performance and best way to go about it.
class X {
public $str;
function __construct($str) {
$this->str = $str;
}
}

Related

PHP returning references

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.

PHP: how to force property of method to be integer?

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.

PHP and MagicMethods

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.

What is the proper way to initialize empty strings in PHP?

In C#, I've come to adopt the following method of initializing empty strings:
string account = string.empty;
rather than
string account = "";
According to my mentor and other C# developers I've talked to, the first method is the better practice.
That said, is there a better way to initialize empty strings in PHP? Currently, I see the following widely used:
$account = '';
Thanks.
What you're doing is correct. Not much more to say about it.
Example:
$account = '';
if ($condition) $account .= 'Some text';
echo $account;
You could get silly and do something like this:
$str = (string) NULL;
..but that's utterly pointless, and it's exactly the same thing - an empty string.
You're doing it right.
For the most part this is irrelevant. Unlike many languages, in PHP it (usually) doesn't matter whether you initialize a variable. PHP will automatically cast an uninitialized (or even undeclared) variable as appropriate for the immediate use. For example, the following are all correct:
$a;
$a + 7; // Evaluates to 7
$a . "This is a test."; // Evaluates to "This is a test."
if (! $a) {} // Evaluates as true
The one caveat is that select functions check for variable type (as does strict equality checking, ===). For example, the following fails:
$a;
if (is_string($a)) {
print 'success';
}
else {
print 'fail';
}
This convenience comes at a heavy cost, though. Unlike strictly typed (or, at least, "more strictly" typed) languages, there is nothing in the core language itself to help you catch common programmer errors. For example, the following will happily execute, but probably not as expected:
$isLoggedIn = getLoginStatus($user);
if ($isLogedIn) {
// Will never run
showOrder($user);
}
else {
showLoginForm();
}
If you choose to initialize all your variables, do it just as you did. But then enable PHP notices (E_NOTICE) to get run-time warnings about uninitialized variables. If you don't, you're basically wasting time and keystrokes initializing your own variable.
Here are some other things to consider when working with strings in PHP:
// Localize based of possible existence
$account = (array_key_exists('account', $results)) ? $results['account'] : null;
// Check to see if string was actually initialized
return (isset($account)) ? $account : null
// If a function is passed an arg which is REQUIRED then validate it
if (empty($arg1)) {
throw new Exception('Invalid $arg1');
}
echo $arg;
// If you are looking to append to string, then initialize it as you described
$account = null;
if (!empty($firstName)) {
$account .= $firstName;
}
echo $account;
// Also, it's better to initialize as null, so you an do simple check constructs
if (is_null($account)) {
// Do something
}
// Versus these types of checks
if ($account == '') {
// Do something
}
Normally I try to avoid initializing vars like this. Instead I localize, or check for existence throughout the code, otherwise you end up maintaining a laundry list of variables which may not actually reflect usage throughout the code following initialization.
chr(32) represents ASCII space (i.e. string of 1 byte length).
If you want to avoid mistakes like $myEmpty = " " vs. $myEmpty = " " vs. $myEmpty = ""
Sometimes it's hard to tell when there are two spaces or one or none by human eyes. Using chr function that is solved for sure.
And for really empty string (zero bytes), there's no other way but to simply define it with (single) quotation marks like $nothing = '';

Testing optional arguments in PHP

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.

Categories