I'm learned php as functional and procedure language. Right now try to start learn objective-oriented and got an important question.
I have code:
class car {
function set_car($model) {
$this->model = $model;
}
function check_model()
{
if($this->model == "Mercedes") echo "Good car";
}
}
$mycar = new car;
$mycar->set_car("Mercedes");
echo $mycar->check_model();
Why it does work without declaration of $model?
var $model; in the begin?
Because in php works "auto-declaration" for any variables?
I'm stuck
Every object in PHP can get members w/o declaring them:
$mycar = new car;
$mycar->model = "Mercedes";
echo $mycar->check_model(); # Good car
That's PHP's default behaviour. Those are public. See manual.
Yes, if it doesn't exist, PHP declares it on the fly for you.
It is more elegant to define it anyway, and when working with extends it's recommended, because you can get weird situations if your extends are gonna use the same varnames and also don't define it private, protected or public.
More info:
http://www.php.net/manual/en/language.oop5.visibility.php
PHP class members can be created at any time. In this way it will be treated as public variable. To declare a private variable you need to declare it.
Yes. But this way variables will be public. And declaration class variable as "var" is deprecated - use public, protected or private.
No, it's because $model is an argument of the function set_car. Arguments are not exactly variables, but placeholders (references) to the variables or values that will be set when calling the function (or class method). E.g., $model takes the value "Mercedes" when calling set_car.
I think this behavior can lead to errors.
Lets consider this code with one misprint
declare(strict_types=1);
class A
{
public float $sum;
public function calcSum(float $a, float $b): float
{
$this->sum = $a;
$this->sums = $a + $b; //misprinted sums instead of sum
return $this->sum;
}
}
echo (new A())->calcSum(1, 1); //prints 1
Even I use PHP 7.4+ type hints and so one, neither compiler, nor IDE with code checkers can't find this typo.
Related
I've a question about function declaration. I would like have something like:
password_hash('er', PASSWORD_ARGON2I);
https://www.php.net/manual/en/function.password-hash.php
When I create a function, I would like declare some possibilities like:
PASSWORD_DEFAULT = 1
PASSWORD_BCRYPT = 2
PASSWORD_ARGON2I = 3
PASSWORD_ARGON2ID = 4
Then, when the user use the function he just set the value of the constant or the constant.
Actually I do:
$instanceOfMyClass->myFunction('er', MyClass::A_CONSTANT);
It done the job, but I'm forced to write the class name before use the constant name. Then if I do thaht I've access to all the constant of the class.
I think, I talk about something like constants at the function level.
Thanks a lot :-)
PS: Is there a way to do a DeclarationType like int|object|string for a function attribute ? Actually I don't type hint in this case, and for object, I need to specify a class or an Interface, sometimes, I accept many object to call the __toString() magic class, then I accept all objects.
I don't really understand your issue
constants like PASSWORD_DEFAULT, but just it (no function name with double :).
but my guess is, you are looking for define().
I suggest you to utilize OOP as much as possible and so like others said, use class constants. But if you want global constants for whatever reason, it goes like this:
define('DIRECTION_FORWARD', 0);
define('DIRECTION_LEFT', 1);
define('DIRECTION_RIGHT', 2);
function goInDirection($direction = DIRECTION_FORWARD)
{
// ....
}
Instead of just sequential numbers as values, you can use bitmasks, which work with bitwise operators and the power of 2.
https://www.php.net/manual/en/language.operators.bitwise.php
There are many ways of doing it.
the defined constants can not be modified, but they can be analyzed and verified if a condition is met; although this one is doing all that; since it would be double work for something immutable / immutable
<?php
class Main{
const CONSTANT = 'value constant';
function ouputConts() {
echo var_dump(self::CONSTANT);
}
}
in your case; that I can keep in a constant:
<?php
class Main{
const CONSTANT1 = 1; //integer
const CONSTANT2 = 0.1345; //float
const CONSTANT3 = 'text'; //string
const CONSTANT4 = array(1,2,3,4,5,10,20); //array
const CONSTANT5 = true; //bollean
function ouputConts() {
echo var_dump(self::CONSTANT1);
echo var_dump(self::CONSTANT2);
echo var_dump(self::CONSTANT3);
echo var_dump(self::CONSTANT4);
echo var_dump(self::CONSTANT5);
}
}
because in php an object or any type of object is considered mutable; you can not declare constants of type objects.
Documentation:
https://php.net/manual/en/language.constants.syntax.php
https://www.php.net/manual/en/language.oop5.constants.php
So your other question is whether you can pass a constant as a property of a function:
Yes, but you should have some considerations:
If you are in the same class it is not necessary you can use the value in any function of the same class, without needing to pass it is Global for the class / Scope:
class Main{
const CONSTANT = 'value constant';
function function1() {
$this->function2();
}
function function2() {
$this->function3();
}
function function3() {
$this->function4();
}
function function4() {
echo var_dump(self::CONSTANT);
}
}
If the function belongs to another class I recommend you first store it in a variable ...:
class Main1{
const CONSTANT = 'value constant';
function function1() {
$foo = CONSTANT;
Main2::FunctionTest($foo);
}
}
class Main2{
function FunctionTest($foo = '') {
echo var_dump($foo);
}
}
// I can not verify it.
I hope I have helped you in your question; if you need more help or deepen your query leave me a comment.
Update; i see this commnet:
#ChristophBurschka By doing that, when you use the function you don't
have help for retrieve specific constant (all class consant are not
available for this function). When you use native PHP function, you
just have possibilities to use available constant and the IDE have the
capatibilities to find them (and it list it to you automatically when
you start to fill the corresponding argument)? Thanks for your answer
about Type hint :-)
if the problem is around the scope of your const:
You can declare global constants in your applications (out of all classes and functions):
<?php
if (!defined('DB_PASS')) {
define('DB_PASS', 'yourpassword');
}
It will be available in any class and function of the whole application. and its use is not to be feared, since they are IMMUTABLE CONSTANTS.
Update recovery and implementation
With the last way I explain, you can then make a comparison and define which data you want to use; if the data of the constant or the data sent by the user; but all this must be tied to an analysis of the user's data in a specific function that you define.
Last Update
With the new define you can this:
<?php
//declaration:
if (!defined('PASS_SIS')) {
define('PASS_SIS', array(
'PASSWORD_DEFAULT' => 1,
'PASSWORD_BCRYPT' => 2,
'PASSWORD_ARGON2I' => 3,
'PASSWORD_ARGON2ID' => 4,
));
}
//call Function
$instanceOfMyClass->myFunction('er');
//function
function myFunction('er'){
echo var_dump(PASS_SIS);
//Access to data:
echo PASS_SIS['PASSWORD_DEFAULT'];
}
First, take a look at this PHP 5.5.8 code which implements lazy initialization of class properties with using a Trait:
trait Lazy
{
private $__lazilyLoaded = [];
protected function lazy($property, $initializer)
{
echo "Initializer in lazy() parameters has HASH = "
. spl_object_hash($initializer) . "\n";
if (!property_exists($this, $property)
|| !array_key_exists($property, $this->__lazilyLoaded))
{
echo "Initialization of property " . $property . "\n";
$this->__lazilyLoaded[$property] = true;
$this->$property = $initializer();
}
return $this->$property;
}
}
class Test
{
use Lazy;
private $x = 'uninitialized';
public function x()
{
return $this->lazy('x', function(){
return 'abc';
});
}
}
echo "<pre>";
$t = new Test;
echo $t->x() . "\n";
echo $t->x() . "\n";
echo "</pre>";
The output is as follow:
uninitialized
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
Initialization of property x
abc
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
abc
Here are my questions and things I'd like to discuss and improve, but I don't know how.
Based on the HASH values reported, it may appear that the initializer function is created only once.
But actually uniqueness is not guaranteed between objects that did not reside in memory simultaneously. So the question remains unanswered - whether the initializer gets created only once, and it matters for performance I think, but I'm not sure.
The way it's implemented now is not very safe in that if I refactor the code and change property $x to something else, I might forget to change the 'x' value as a first parameter to lazy() method. I'd be happy to use & $this->x instead as a first parameter, but then inside lazy() function I don't have a key to use for $__lazilyLoaded array to keep track of what has been initialized and what has not. How could I solve this problem? Using hash as a key isn't safe, nor it can be generated for callbacks like array($object, 'methodName')
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to access the raw $this->x property as it can be still uninitialized. So I wonder is there a better way - maybe I should save all the values in some Trait's field?
The global aim is to make it:
a) Fast - acceptable enough for small and medium software applications
b) Concise in syntax - as much as possible, to be used widely in the methods of the classes which utilize the Lazy trait.
c) Modular - it would be nice if objects still held their own properties; I don't like the idea of one super-global storage of lazily-initialized values.
Thank you for your help, ideas and hints!
So the question remains unanswered - whether the
initializer gets created only once, and it matters for performance I
think, but I'm not sure.
Well, closure instance is created only once. But anyway, performance will depend not on closure instance creation time (since it is insignificant), but closure execution time.
I'd be happy to use & $this->x instead as a first parameter, but then
inside lazy() function I don't have a key to use for $__lazilyLoaded
array to keep track of what has been initialized and what has not. How
could I solve this problem? Using hash as a key isn't safe, nor it can
be generated for callbacks like array($object, 'methodName')
I can propose the following solution:
<?php
trait Lazy
{
private $_lazyProperties = [];
private function getPropertyValue($propertyName) {
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
if(!isset($this->_propertyLoaders[$propertyName])) {
throw new Exception("Property $propertyName does not have loader!");
}
$propertyValue = $this->_propertyLoaders[$propertyName]();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
public function __call($methodName, $arguments) {
if(strpos($methodName, 'get') !== 0) {
throw new Exception("Method $methodName is not implemented!");
}
$propertyName = substr($methodName, 3);
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
$propertyInializerName = 'lazy' . $propertyName;
$propertyValue = $this->$propertyInializerName();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
}
/**
* #method getX()
**/
class Test
{
use Lazy;
protected function lazyX() {
echo("Initalizer called.\r\n");
return "X THE METHOD";
}
}
echo "<pre>";
$t = new Test;
echo $t->getX() . "\n";
echo $t->getX() . "\n";
echo "</pre>";
Result:
c:\Temp>php test.php
<pre>X THE METHOD
X THE METHOD
</pre>
c:\Temp>php test.php
<pre>Initalizer called.
X THE METHOD
X THE METHOD
</pre>
c:\Temp>
You cannot always be protected from forgetting something, but it is easier to remember when all things are close to each other. So, I propose to implement lazy loaders as methods on corresponding classes with specific names. To provide autocomplete #method annotation can be used. In a good IDE refactoring method name in annotation will allow to rename method across all project. Lazy loading function will be declared in the same class so renaming it also is not a problem.
By declaring a function with a name, starting with "lazy", in my example you both declare a corresponding accessor function, with name starting with "get" and it's lazy loader.
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to
access the raw $this->x property as it can be still uninitialized. So
I wonder is there a better way - maybe I should save all the values in
some Trait's field?
Trait fields are available in the class, that uses specific trait. Even private fields. Remember, this is composition, not inheritance. I think it's better to create private trait array field and store your lazy properties there. No need to create a new field for every property.
But I cannot say I like the whole scheme. Can you explain the use of it for you? May be we can come with better solution.
I have a string containing the class name and I wish to get a constant and call a (static) method from that class.
<?php
$myclass = 'b'; // My class I wish to use
$x = new x($myclass); // Create an instance of x
$response = $x->runMethod(); // Call "runMethod" which calls my desired method
// This is my class I use to access the other classes
class x {
private $myclass = NULL;
public function __construct ( $myclass ) {
if(is_string($myclass)) {
// Assuming the input has a valid class name
$this->myclass = $myclass;
}
}
public function runMethod() {
// Get the selected constant here
print $this->myclass::CONSTANT;
// Call the selected method here
return $this->myclass::method('input string');
}
}
// These are my class(es) I want to access
abstract class a {
const CONSTANT = 'this is my constant';
public static function method ( $str ) {
return $str;
}
}
class b extends a {
const CONSTANT = 'this is my new constant';
public static function method ( $str ) {
return 'this is my method, and this is my string: '. $str;
}
}
?>
As I expected (more or less), using $variable::CONSTANT or $variable::method(); doesn't work.
Before asking what I have tried; I've tried so many things I basically forgot.
What's the best approach to do this? Thanks in advance.
To access the constant, use constant():
constant( $this->myClass.'::CONSTANT' );
Be advised: If you are working with namespaces, you need to specifically add your namespace to the string even if you call constant() from the same namespace!
For the call, you'll have to use call_user_func():
call_user_func( array( $this->myclass, 'method' ) );
However: this is all not very efficient, so you might want to take another look at your object hierarchy design. There might be a better way to achieve the desired result, using inheritance etc.
in php 7 you can use this code
echo 'my class name'::$b;
or
#Uncomment this lines if you're the input($className and $constName) is safe.
$reg = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/';
if(preg_match($reg,$className) !== 1 || preg_match($reg,$constName) !== 1)
throw new \Exception('Oh, is it an attack?');
$value = eval("return $className::$constName;");
You can achieve it by setting a temporary variable. Not the most elegant way but it works.
public function runMethod() {
// Temporary variable
$myclass = $this->myclass;
// Get the selected constant here
print $myclass::CONSTANT;
// Call the selected method here
return $myclass::method('input string');
}
I guess it's to do with the ambiguity of the ::, at least that what the error message is hinting at (PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM)
Use call_user_func to call static method:
call_user_func(array($className, $methodName), $parameter);
Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature - they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private. Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures could differ.
Refer to http://php.net/manual/en/language.oop5.abstract.php
This might just be tangential to the subject but, while searching for my own issue I found that the accepted answer pointed me in the right direction, so I wanted to share my problem & solution in case someone else might be stuck in a similar fashion.
I was using the PDO class and was building some error options from an ini config file. I needed them in an associative array in the form: PDO::OPTION_KEY => PDO::OPTION_VALUE, but it was of course failing because I was trying to build the array with just PDO::$key => PDO::$value.
The solution (inspired from the accepted answer):
$config['options'] += [constant('PDO::'.$key) => constant('PDO::'.$option)];
where everything works if you concatenate the class name and the Scope Resolution Operator as a string with the variable and get the constant value of the resulting string through the constant function (more here).
Thank you and I hope this helps someone else!
Sorry this is going to sound like a ridiculous question but can methods (not the constructor) in classes take parameters in the function declaration?
All the examples I've seen of methods (not constructor) pass no variables but call variables already declared in the class with $this->someVariable inside the function body.
Yes, you certainly can:
class Foo {
public function sum($a, $b, $c) {
$sum = $a + $b + $c;
return $sum;
}
}
$foo = new Foo();
echo $foo->sum(1,2,3); //Displays 6
Of course, like any other function.
<?php
class Foo {
public function displayParameter($param) {
return $param;
}
}
$foo = new Foo();
echo $foo->displayParameter("Hello World"); //Displays Hello World
?>
It depends. If a class represents some object, then you will have properties that can be accessed by the functions (methods). Static methods often accept parameters. So the answer is, yes, methods can accept parameters, but it also depends on how you use the class.
can methods (not the constructor) in classes take parameters in the function declaration?
Yes.
Yes. You can pass parameters directly or by reference if you want to change their value within the called method. Let me recommend a bit of reading: http://php.net/manual/en/index.php
I've come across some unexpected behavior with static variables defined inside object methods being shared across instances. This is probably known behavior, but as I browse the PHP documentation I can't find instances of statically-defined variables within object methods.
Here is a reduction of the behavior I've come across:
<?php
class Foo {
public function dofoo() {
static $i = 0;
echo $i++ . '<br>';
}
}
$f = new Foo;
$g = new Foo;
$f->dofoo(); // expected 0, got 0
$f->dofoo(); // expected 1, got 1
$f->dofoo(); // expected 2, got 2
$g->dofoo(); // expected 0, got 3
$g->dofoo(); // expected 1, got 4
$g->dofoo(); // expected 2, got 5
Now, I would have expected $i to be static per instance, but in reality $i is shared between the instances. For my own edification, could someone elaborate on why this is the case, and where it's documented on php.net?
This is the very definition of static.
If you want members to be specific to an instance of an object, then you use class properties
e.g.
<?php
class Foo
{
protected $_count = 0;
public function doFoo()
{
echo $this->_count++, '<br>';
}
}
Edit: Ok, I linked the documentation to the OOP static properties. The concept is the same though. If you read the variable scope docs you'll see:
Note: Static declarations are resolved in compile-time.
Thus when your script is compiled (before it executes) the static is "setup" (not sure what term to use). No matter how many objects you instantiate, when that function is "built" the static variable references the same copy as everyone else.
I agree that the current PHP documentation is not sufficiently clear on exactly what "scope" means for a static variable inside a non-static method.
It is of course true (as hobodave indicates) that "static" generally means "per class", but static class properties are not exactly the same thing as static variables within a (non static) method, in that the latter are "scoped" by method (every method in a class can have its own static $foo variable, but there can be at most one static class member named $foo).
And I would argue that although the PHP 5 behavior is consistent ("static" always means "one shared instance per class"), it is not the only way that PHP could behave.
For example, most people use static function variables to persist state across function calls, and for global functions the PHP behavior is exactly what most everyone would expect. So it is certainly possible to imagine a PHP interpreter that maintains the state of certain method variables across method invocation and does so "per instance", and that's actually what I also expected to happen the first time I declared a local method variable to be static.
That is what static is, it's the same variable across all instances of the class.
You want to write this so that the variable is a private member of the instance of the class.
class Foo {
private $i = 0;
public function dofoo() {
echo $this->i++ . '<br>';
}
}
The static keyword can be used with variables, or used with class methods and properties. Static variables were introduced in PHP 4 (I think, it might have been earlier). Static class members/methods were introduced in PHP 5.
So, per the manual, a static variable
Another important feature of variable scoping is the static
variable. A static variable exists only in a local function
scope, but it does not lose its value when program execution
leaves this scope.
This is consistant with the behavior you described. If you want a per instance variable, used a regular class member.
Ups 7 years it a long time but anyway here it goes.
All classes have a default constructor why am I saying this?!?
Because if you define a default behaviour in constructor each instance of the class will be affected.
Example:
namespace Statics;
class Foo
{
protected static $_count;
public function Bar()
{
return self::$_count++;
}
public function __construct()
{
self::$_count = 0;
}
}
Resulting in:
require 'Foo.php';
use Statics\Foo;
$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
0
1
2
0
1
2
Every new instance from the upper class will start from 0!
The static count behaviour will NOT be shared across the multiple instances as it will be starting from the value assigned in constructor.
If you need to share data across multiple instances all you need to do is to define a static variable and assign default data outside the constructor!
Example:
namespace Statics;
class Foo
{
//default value
protected static $_count = 0;
public function Bar()
{
return self::$_count++;
}
public function __construct()
{
//do something else
}
}
Resulting in:
require 'Foo.php';
use Statics\Foo;
$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
0
1
2
3
4
5
As you can see the results are completely different, the memory space allocation is the same in between class instances but it can produce different results based on how you define default value.
I hope it helped, not that the above answers are wrong but I felt that it was important to understand the all concept from this angle.
Regards, from Portugal!