You can invoke a class above its definitions in some cases, but there are very important exceptions to this behavior. If your class extends another class, and the interpreter doesn't see the extended class first, then it won't be added to the symbol table until the code has stepped through it in runtime. As a result, if you try to invoke it above the definition, you'll get a "class not found" fatal error.
And those suck.
So, to provide an example, the following will output a fatal error
<?php
Bar::bar();
exit;
class Bar extends Foo { } //will fatal because Foo not seen yet
class Foo {
static function bar() { echo 'yep, this is Foo::bar'; }
}
?>
However, THIS code will work just fine:
<?php
Bar::bar();
exit;
class Foo {
static function bar() { echo 'yep, this is Foo::bar'; }
}
class Bar extends Foo { } //will work because Foo came first
?>
Notice that if you include a file containing the class you will extend, and then extend the class in the same file as its invocation, you can also get the class not found fatal. This happens even if the 'include' call happens before the child class's definition.
Eg. the following will also output a fatal error
<?php
include_once('file_with_foo.php');
Bar::bar();
exit;
class Bar extends Foo { }
?>
Hope that clarifies things.
class
Класът е набор от променливи и функции, които работят с променливите. Променливите се дефинират с ключовата дума var, а функциите с function. Дефиницията на клас има следния синтаксис:
<?php
class Cart {
var $items; // Артикули в нашата пазарска количка
// Добавяне на $num артикули от $artnr към пазарската количка
function add_item($artnr, $num) {
$this->items[$artnr] += $num;
}
// Премахване на $num артикула от $artnr от пазарската количка
function remove_item($artnr, $num) {
if ($this->items[$artnr] > $num) {
$this->items[$artnr] -= $num;
return true;
} elseif ($this->items[$artnr] == $num) {
unset($this->items[$artnr]);
return true;
} else {
return false;
}
}
}
?>
В примера по-горе е дефиниран клас Cart, който се състои от асоциативен масив, от артикули на пазарска количка и две функции за добавяне и премахване на артикули от тази пазарска количка.
Не може дефиницията на клас да се помества в множество файлове. Също така не може дефиницията на клас да се помества в множество блокове PHP, освен ако прекъсването не е в декларацията на метод. Следният пример няма да работи:
<?php
class test {
?>
<?php
function test() {
print 'OK';
}
}
?>
Следното обаче е позволено:
<?php
class test {
function test() {
?>
<?php
print 'OK';
}
}
?>
Следните предупредителни забележки са валидни за PHP 4.
Наименованието stdClass се използва вътрешно от Zend и е запазено. Не можете да имате клас с име stdClass в PHP.
Имената на функциите __sleep и __wakeup са вълшебни в класовете на PHP. Не може да имате функции с тези имена във вашите класове, освен ако не искате да ползвате вълшебната функционалност свързана с тях. Вижте по-долу за повече информация.
PHP запазва всички имена на функции, започващи с __ като вълшебни. Препоръчително е да не използвате имена на функции в PHP, започващи с __, освен ако не искате да ползвате документираната вълшебна функционалност.
В PHP 4 var-променливите на класовете могат да се инициализират само с константни стойности. За да се инициализира променлива с неконстантна стойност, трябва да се използва инициализационна функция, която се извиква автоматично при инстанциирането на класа. Такава функция се нарича конструктор (вижте по-долу).
<?php
class Cart {
/* Нито една от долните конструкции няма да работи в PHP 4. */
var $todays_date = date("Y-m-d");
var $name = $firstname;
var $owner = 'Fred ' . 'Jones';
/* Масивите съдържащи константни стойности ще работят. */
var $items = array("VCR", "TV");
}
/* Това е начинът да се направи. */
class Cart {
var $todays_date;
var $name;
var $owner;
var $items = array("VCR", "TV");
function Cart() {
$this->todays_date = date("Y-m-d");
$this->name = $GLOBALS['firstname'];
/* и т.н. */
}
}
?>
Класовете са типове, което означава, че са шаблони за действителните променливи. Можете да създадете променлива от желаният от вас тип чрез оператора new
<?php
$cart = new Cart;
$cart->add_item("10", 1);
$another_cart = new Cart;
$another_cart->add_item("0815", 3);
?>
В горния пример се създават обектите $cart и $another_cart като и двата са инстанции на класа Cart. Функцията add_item() на обекта $cart се извиква за да се добави един артикул с номер 10 към $cart. Също така се добавят 3 артикула с номер 0815 към $another_cart.
И двата обекта - $cart и $another_cart, притежават функции add_item(), remove_item() и променливи. Можете да си представите обектите като нещо подобно на директориите в една файлова система. В дадена файлова система може да имате 2 отделни файла README.TXT, стига да се намират в различни директории. Също както при директориите ще трябва да въведете пълния път до файла, за да имате достъп до него от главната директория, трябва да зададете пълното име на функцията която искате да извикате: в контекста на PHP - главната директория ще бъде глобалното пространство от имена, а разделителят в името на пътя се явява ->. По този начин $cart->items и $another_cart->items са две различни променливи. Забележете, че променливата е $cart->items, а не $cart->$items, което означава, че името на променливата в PHP трябва да има само един знак $.
<?php
// правилно, единичен $
$cart->items = array("10" => 1);
// невалидно, понеже $cart->$items става $cart->""
$cart->$items = array("10" => 1);
// правилно, но резултата може да не е този, който очакваме:
// $cart->$myvar става $cart->items
$myvar = 'items';
$cart->$myvar = array("10" => 1);
?>
В дефиницията на класа вие не знаете името на обекта който ще го инстанциира: по времето на написването на класа Cart не беше известно, че по-късно обектът ще бъде кръстен $cart или $another_cart. Следователно няма как да напишете $cart->items в самия клас. Вместо това, за да имаме достъп до собствените функции и променливи на класа, може да използваме псевдо-променливата $this, която е референция към обекта, който е инстанциирал класа. Следователно '$this->items[$artnr] += $num' може да бъде прочетено като 'добави $num към брояча $artnr на артикулите на моя масив' или 'добави $num към брояча $artnr на артикулите на масива за текущия обект'.
Забележка: Псевдо-променливата $this обикновено не е дефинирана ако метода в който се намира е извикан статично. Това все пак не е стриктно правило: $this е дефинирана ако метода е извикан статично от друг обект. В този случай, стойността на $this е тази на извикващият обект. Това е илюстрирано със следният пример:
<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this е дефинирана (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this не е дефинирана.\n";
}
}
}
class B
{
function bar()
{
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>Примерът по-горе ще изведе:
$this е дефинирана (a) $this не е дефинирана. $this е дефинирана (b) $this не е дефинирана.
Забележка: Съществуват няколко функции за работа с класове и обекти. Можете да ги разгледате в раздел Функции за класове и обекти.
I just discovered a behaviour that has to be mentioned.
Let's consider the following file
<?php
Foo::bar();
exit;
class Foo {
function bar() { echo 'yep, this is Foo::bar'; }
}
?>
You may expect :
- an error on the Foo::bar call, because the class is not defined
- removing the code after the exit without side-effect
But it won't, and it will output the string !
It seems every class definition is executed at parse-time.
But without errors; if your file is
<?php
Foo::bar();
exit;
class Foo {
function bar() { echo 'yep, this is Foo::bar'; }
}
class Foo {
function bar() { echo 'yep, this is another Foo::bar'; }
}
?>
it will still output the first string, not doing any "Already exiting class" error !
So if you intend preventing a double include by doing in an included file :
<?php
if (class_exists('Foo')) { return; }
class Foo {
static function register() { ... }
}
Foo::register();
?>
the Foo::register method won't be called !
Very strange development choice; I suppose it is due to ascending compatibility.
Hope it can help...
You can also instantiate objects using variables containing the name of the class:
<?php
$type = 'Foo';
$foo = new $type();
print_r( $foo );
/* Prints:
Foo Object
(
)
*/
?>
The following works as well:
<?php
$somefoo = new Foo();
$anotherfoo = new $somefoo();
# so you don't have to do:
$anotherfoo = new get_class( $somefoo)();
?>
