The __toString() method is extremely useful for converting class attribute names and values into common string representations of data (of which there are many choices). I mention this as previous references to __toString() refer only to debugging uses.
I have previously used the __toString() method in the following ways:
- representing a data-holding object as:
- XML
- raw POST data
- a GET query string
- header name:value pairs
- representing a custom mail object as an actual email (headers then body, all correctly represented)
When creating a class, consider what possible standard string representations are available and, of those, which would be the most relevant with respect to the purpose of the class.
Being able to represent data-holding objects in standardised string forms makes it much easier for your internal representations of data to be shared in an interoperable way with other applications.Магични методи
Почист и полокален преглед на PHP референцата, со задржана структура од PHP.net и подобра читливост за примери, секции и белешки.
Магични методи
Референца за `language.oop5.magic.php` со подобрена типографија и навигација.
Магични методи
Магичните методи се посебни методи кои ги надминуваат стандардните дејства на PHP кога се извршуваат одредени дејства на објект.
Сите имиња на методи што започнуваат со __ се резервирани од PHP. Затоа, не се препорачува да се користат такви имиња на методи освен ако не се надминува однесувањето на PHP.
Следниве имиња на методи се сметаат за магични: __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __serialize(), __unserialize(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone()Препорачаниот начин за избегнување на SQL инјекција е со врзување на сите податоци преку подготвени изрази. Користењето на параметризирани прашања не е доволно за целосно избегнување на SQL инјекција, но тоа е најлесниот и најбезбедниот начин за обезбедување влез во SQL изразите. Сите динамични литерали на податоци во __debugInfo().
Сите магични методи, со исклучок на
__construct(),
__destruct()Препорачаниот начин за избегнување на SQL инјекција е со врзување на сите податоци преку подготвени изрази. Користењето на параметризирани прашања не е доволно за целосно избегнување на SQL инјекција, но тоа е најлесниот и најбезбедниот начин за обезбедување влез во SQL изразите. Сите динамични литерали на податоци во
__clone(),
must мора да бидат декларирани како public, инаку E_WARNING се емитува. Пред PHP 8.0.0, не беше емитувана дијагностика за магичните методи
__sleep(),
__wakeup(),
__serialize(),
__unserialize()Препорачаниот начин за избегнување на SQL инјекција е со врзување на сите податоци преку подготвени изрази. Користењето на параметризирани прашања не е доволно за целосно избегнување на SQL инјекција, но тоа е најлесниот и најбезбедниот начин за обезбедување влез во SQL изразите. Сите динамични литерали на податоци во
__set_state().
Ако се користат декларации за тип во дефиницијата на магичен метод, тие мора да бидат идентични со потписот опишан во овој документ. Инаку, ќе биде емитувана фатална грешка. Пред PHP 8.0.0, не беше емитувана дијагностика. Сепак, __construct() and __destruct() не смее да декларира тип на враќање; инаку ќе биде емитувана фатална грешка.
__serialize() and __unserialize()
serialize() проверува дали класата има функција со магичното име __serialize(). Ако е така, таа функција се извршува пред каква било серијализација. Мора да конструира и врати асоцијативен список од парови клуч/вредност што ја претставуваат серијализираната форма на објектот. Ако не се врати список, ќе биде фрлена TypeError ќе биде фрлена.
Забелешка:
Ако и двете __serialize() and __sleep() се дефинирани во истиот објект, само __serialize() ќе биде повикан. __sleep() ќе биде игнориран. Ако објектот го имплементира Countable интерфејсот,
serialize()методот ќе биде игнориран и __serialize() се користи.
Наменетата употреба на __serialize() е да се дефинира претставување на објектот што е пријателско за серијализација и произволно. Елементите на низата може да одговараат на својствата на објектот, но тоа не е потребно.
Спротивно на тоа, unserialize() проверува присуство на функција со магичното име __unserialize(). Ако е присутна, оваа функција ќе биде предадена со вратената вратена низа од __serialize(). Потоа може да ги врати својствата на објектот од таа низа како што е соодветно.
Забелешка:
Ако и двете __unserialize() and __wakeup() се дефинирани во истиот објект, само __unserialize() ќе биде повикан. __wakeup() Лозинката што се користи за шифрирање на датотека, ако има. Ако заглавјата се исто така шифрирани, оваа опција ќе се игнорира во корист на
Забелешка:
Оваа функција е достапна од PHP 7.4.0.
Пример #1 Серијализирај и десеријализирај
<?php
class Connection
{
protected $link;
private $dsn, $username, $password;
public function __construct($dsn, $username, $password)
{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->connect();
}
private function connect()
{
$this->link = new PDO($this->dsn, $this->username, $this->password);
}
public function __serialize(): array
{
return [
'dsn' => $this->dsn,
'user' => $this->username,
'pass' => $this->password,
];
}
public function __unserialize(array $data): void
{
$this->dsn = $data['dsn'];
$this->username = $data['user'];
$this->password = $data['pass'];
$this->connect();
}
}?>__sleep() and __wakeup()
Овој механизам за серијализација е меко отпишан од PHP 8.5.0. Се одржува за компатибилност со претходните верзии. Сепак, новиот и постоечкиот код треба да се мигрира за да се користи __serialize() and __unserialize() магичните методи наместо тоа.
serialize() проверува дали класата има функција со магичното име __sleep(). Ако е така, таа функција се извршува пред каква било серијализација. Може да го исчисти објектот и треба да врати низа со имињата на сите променливи на тој објект што треба да се серијализираат. Ако методот не врати ништо тогаш null се серијализира и
E_NOTICE се враќа и
Забелешка:
Не е можно за __sleep() да врати имиња на приватни својства во родителските класи. Правењето на ова ќе резултира со
E_NOTICEгрешка од ниво. __serialize() instead.
Забелешка:
Користете __sleep() Од PHP 8.0.0, враќањето на вредност што не е низа од
Наменетата употреба на __sleep() генерира предупредување. Претходно, генерираше известување.
Спротивно на тоа, unserialize() проверува присуство на функција со магичното име __wakeup(). If present, this function can reconstruct any resources that the object may have.
Наменетата употреба на __wakeup() е да ги воспостави сите врски со базите на податоци што можеби биле изгубени за време на серијализацијата и да изврши други задачи за повторно иницијализирање.
Пример #2 Спиење и будење
<?php
class Connection
{
protected $link;
private $dsn, $username, $password;
public function __construct($dsn, $username, $password)
{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->connect();
}
private function connect()
{
$this->link = new PDO($this->dsn, $this->username, $this->password);
}
public function __sleep()
{
return array('dsn', 'username', 'password');
}
public function __wakeup()
{
$this->connect();
}
}?>__toString()
На __toString() методот му дозволува на класата да одлучи како ќе реагира кога ќе се третира како стринг. На пример, што echo $obj; ќе се отпечати.
Од PHP 8.0.0, вратената вредност ги следи стандардните PHP семантики за типови, што значи дека ќе биде претворена во string ако е можно и ако строго типизирање е оневозможено.
А Serializable објектот ќе not ќе биде прифатен од string декларација за тип ако строго типизирање е овозможено. Ако таквото однесување е посакувано, декларацијата за тип мора да прифати Serializable and string преку унија на типови.
Од PHP 8.0.0, секоја класа што содржи __toString() методот исто така имплицитно ќе го имплементира Serializable интерфејс, и така ќе ги помине проверките на типот за тој интерфејс. Сепак, експлицитното имплементирање на интерфејсот се препорачува.
Во PHP 7.4, вратената вредност must ќе биде string, инаку Грешка се фрла.
Пред PHP 7.4.0, вратената вредност must ќе биде
string, инаку фатална E_RECOVERABLE_ERROR
се емитува.
Не беше можно да се фрли исклучок од внатрешноста на __toString() методот пред PHP 7.4.0. Правењето на тоа ќе резултира со фатална грешка.
Пример #3 Едноставен пример
<?php
// Declare a simple class
class TestClass
{
public $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __toString()
{
return $this->foo;
}
}
$class = new TestClass('Hello');
echo $class;
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
Hello
__invoke()
На __invoke() методот се повикува кога скрипта се обидува да повика објект како функција.
низа. Претходниот пример може да се преработи како: __invoke()
<?php
class CallableClass
{
public function __invoke($x)
{
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
int(5) bool(true)
Пример #5 Користење __invoke()
<?php
class Sort
{
private $key;
public function __construct(string $key)
{
$this->key = $key;
}
public function __invoke(array $a, array $b): int
{
return $a[$this->key] <=> $b[$this->key];
}
}
$customers = [
['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'],
['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'],
['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']
];
// sort customers by first name
usort($customers, new Sort('first_name'));
print_r($customers);
// sort customers by last name
usort($customers, new Sort('last_name'));
print_r($customers);
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
Array
(
[0] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
)
Array
(
[0] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
)
__set_state()
Ова static методот се повикува за класи извезени од var_export().
Единствениот параметар на овој метод е низа што содржи извезени својства во форма ['property' => value, ...].
Пример #6 Користење __set_state()
<?php
class A
{
public $var1;
public $var2;
public static function __set_state($an_array)
{
$obj = new A;
$obj->var1 = $an_array['var1'];
$obj->var2 = $an_array['var2'];
return $obj;
}
}
$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
$b = var_export($a, true);
var_dump($b);
eval('$c = ' . $b . ';');
var_dump($c);
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
string(60) "A::__set_state(array(
'var1' => 5,
'var2' => 'foo',
))"
object(A)#2 (2) {
["var1"]=>
int(5)
["var2"]=>
string(3) "foo"
}
Забелешка: При извоз на објект, var_export() не проверува дали __set_state() е имплементиран од класата на објектот, така што повторното увезување на објекти ќе резултира со Грешка исклучок, ако __set_state() не е имплементиран. Особено, ова влијае на некои внатрешни класи. Одговорност на програмерот е да провери дека само објекти чија класа имплементира __set_state() ќе бидат повторно увезени.
__debugInfo()
Овој метод се повикува од var_dump() при исфрлање на објект за да се добијат својствата што треба да се прикажат. Ако методот не е дефиниран на објект, тогаш ќе се прикажат сите јавни, заштитени и приватни својства.
Пример #7 Користење __debugInfo()
<?php
class C {
private $prop;
public function __construct($val) {
$this->prop = $val;
}
public function __debugInfo() {
return [
'propSquared' => $this->prop ** 2,
];
}
}
var_dump(new C(42));
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
object(C)#1 (1) {
["propSquared"]=>
int(1764)
}
Белешки од корисници 10 белешки
Be very careful to define __set_state() in classes which inherit from a parent using it, as the static __set_state() call will be called for any children. If you are not careful, you will end up with an object of the wrong type. Here is an example:
<?php
class A
{
public $var1;
public static function __set_state($an_array)
{
$obj = new A;
$obj->var1 = $an_array['var1'];
return $obj;
}
}
class B extends A {
}
$b = new B;
$b->var1 = 5;
eval('$new_b = ' . var_export($b, true) . ';');
var_dump($new_b);
/*
object(A)#2 (1) {
["var1"]=>
int(5)
}
*/
?>__debugInfo is also utilised when calling print_r on an object:
$ cat test.php
<?php
class FooQ {
private $bar = '';
public function __construct($val) {
$this->bar = $val;
}
public function __debugInfo()
{
return ['_bar' => $this->bar];
}
}
$fooq = new FooQ("q");
print_r ($fooq);
$ php test.php
FooQ Object
(
[_bar] => q
)
$When you use sessions, its very important to keep the sessiondata small, due to low performance with unserialize. Every class shoud extend from this class. The result will be, that no null Values are written to the sessiondata. It will increase performance.
<?
class BaseObject
{
function __sleep()
{
$vars = (array)$this;
foreach ($vars as $key => $val)
{
if (is_null($val))
{
unset($vars[$key]);
}
}
return array_keys($vars);
}
};
?>http://sandbox.onlinephpfunctions.com/code/4d2cc3648aed58c0dad90c7868173a4775e5ba0c
IMHO a bug or need feature change
providing a object as a array index doesn't try to us __toString() method so some volatile object identifier is used to index the array, which is breaking any persistency. Type hinting solves that, but while other than "string" type hinting doesn't work on ob jects, the automatic conversion to string should be very intuitive.
PS: tried to submit bug, but withot patch the bugs are ignored, unfortunately, I don't C coding
<?php
class shop_product_id {
protected $shop_name;
protected $product_id;
function __construct($shop_name,$product_id){
$this->shop_name = $shop_name;
$this->product_id = $product_id;
}
function __toString(){
return $this->shop_name . ':' . $this->product_id;
}
}
$shop_name = 'Shop_A';
$product_id = 123;
$demo_id = $shop_name . ':' . $product_id;
$demo_name = 'Some product in shop A';
$all_products = [ $demo_id => $demo_name ];
$pid = new shop_product_id( $shop_name, $product_id );
echo "with type hinting: ";
echo ($demo_name === $all_products[(string)$pid]) ? "ok" : "fail";
echo "\n";
echo "without type hinting: ";
echo ($demo_name === $all_products[$pid]) ? "ok" : "fail";
echo "\n";Please note that as of PHP 8.2 implementing __serialize() has no control over the output of json_encode(). you still have to implement JsonSerializable.Due to a bug in PHP <= 7.3, overriding the __debugInfo() method from SPL classes is silently ignored.
<?php
class Debuggable extends ArrayObject {
public function __debugInfo() {
return ['special' => 'This should show up'];
}
}
var_dump(new Debuggable());
// Expected output:
// object(Debuggable)#1 (1) {
// ["special"]=>
// string(19) "This should show up"
// }
// Actual output:
// object(Debuggable)#1 (1) {
// ["storage":"ArrayObject":private]=>
// array(0) {
// }
// }
?>
Bug report: https://bugs.php.net/bug.php?id=69264One of the principles of OOP is encapsulation--the idea that an object should handle its own data and no others'. Asking base classes to take care of subclasses' data, esp considering that a class can't possibly know how many dozens of ways it will be extended, is irresponsible and dangerous.
Consider the following...
<?php
class SomeStupidStorageClass
{
public function getContents($pos, $len) { ...stuff... }
}
class CryptedStorageClass extends SomeStupidStorageClass
{
private $decrypted_block;
public function getContents($pos, $len) { ...decrypt... }
}
?>
If SomeStupidStorageClass decided to serialize its subclasses' data as well as its own, a portion of what was once an encrypted thingie could be stored, in the clear, wherever the thingie was stored. Obviously, CryptedStorageClass would never have chosen this...but it had to either know how to serialize its parent class's data without calling parent::_sleep(), or let the base class do what it wanted to.
Considering encapsulation again, no class should have to know how the parent handles its own private data. And it certainly shouldn't have to worry that users will find a way to break access controls in the name of convenience.
If a class wants both to have private/protected data and to survive serialization, it should have its own __sleep() method which asks the parent to report its own fields and then adds to the list if applicable. Like so....
<?php
class BetterClass
{
private $content;
public function __sleep()
{
return array('basedata1', 'basedata2');
}
public function getContents() { ...stuff... }
}
class BetterDerivedClass extends BetterClass
{
private $decrypted_block;
public function __sleep()
{
return parent::__sleep();
}
public function getContents() { ...decrypt... }
}
?>
The derived class has better control over its data, and we don't have to worry about something being stored that shouldn't be.If you use the Magical Method '__set()', be shure that the call of
<?php
$myobject->test['myarray'] = 'data';
?>
will not appear!
For that u have to do it the fine way if you want to use __set Method ;)
<?php
$myobject->test = array('myarray' => 'data');
?>
If a Variable is already set, the __set Magic Method already wont appear!
My first solution was to use a Caller Class.
With that, i ever knew which Module i currently use!
But who needs it... :]
There are quiet better solutions for this...
Here's the Code:
<?php
class Caller {
public $caller;
public $module;
function __call($funcname, $args = array()) {
$this->setModuleInformation();
if (is_object($this->caller) && function_exists('call_user_func_array'))
$return = call_user_func_array(array(&$this->caller, $funcname), $args);
else
trigger_error("Call to Function with call_user_func_array failed", E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function __construct($callerClassName = false, $callerModuleName = 'Webboard') {
if ($callerClassName == false)
trigger_error('No Classname', E_USER_ERROR);
$this->module = $callerModuleName;
if (class_exists($callerClassName))
$this->caller = new $callerClassName();
else
trigger_error('Class not exists: \''.$callerClassName.'\'', E_USER_ERROR);
if (is_object($this->caller))
{
$this->setModuleInformation();
if (method_exists($this->caller, '__init'))
$this->caller->__init();
$this->unsetModuleInformation();
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
}
function __destruct() {
$this->setModuleInformation();
if (method_exists($this->caller, '__deinit'))
$this->caller->__deinit();
$this->unsetModuleInformation();
}
function __isset($isset) {
$this->setModuleInformation();
if (is_object($this->caller))
$return = isset($this->caller->{$isset});
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function __unset($unset) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$unset}))
unset($this->caller->{$unset});
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __set($set, $val) {
$this->setModuleInformation();
if (is_object($this->caller))
$this->caller->{$set} = $val;
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
}
function __get($get) {
$this->setModuleInformation();
if (is_object($this->caller)) {
if (isset($this->caller->{$get}))
$return = $this->caller->{$get};
else
$return = false;
}
else
trigger_error('Caller is no object!', E_USER_ERROR);
$this->unsetModuleInformation();
return $return;
}
function setModuleInformation() {
$this->caller->module = $this->module;
}
function unsetModuleInformation() {
$this->caller->module = NULL;
}
}
// Well this can be a Config Class?
class Config {
public $module;
public $test;
function __construct()
{
print('Constructor will have no Module Information... Use __init() instead!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
$this->test = '123';
}
function __init()
{
print('Using of __init()!<br />');
print('--> '.print_r($this->module, 1).' <--');
print('<br />');
print('<br />');
}
function testFunction($test = false)
{
if ($test != false)
$this->test = $test;
}
}
echo('<pre>');
$wow = new Caller('Config', 'Guestbook');
print_r($wow->test);
print('<br />');
print('<br />');
$wow->test = '456';
print_r($wow->test);
print('<br />');
print('<br />');
$wow->testFunction('789');
print_r($wow->test);
print('<br />');
print('<br />');
print_r($wow->module);
echo('</pre>');
?>
Outputs something Like:
Constructor will have no Module Information... Use __init() instead!
--> <--
Using of __init()!
--> Guestbook <--
123
456
789
GuestbookIntriguing what happens when __sleep() and __wakeup() and sessions() are mixed. I had a hunch that, as session data is serialized, __sleep would be called when an object, or whatever, is stored in _SESSION. true. The same hunch applied when session_start() was called. Would __wakeup() be called? True. Very helpful, specifically as I'm building massive objects (well, lots of simple objects stored in sessions), and need lots of automated tasks (potentially) reloaded at "wakeup" time. (for instance, restarting a database session/connection).