вторник, 29 апреля 2014 г.

ООП

Класс это так называемый чертеж по которому будет строится класс
при  прописывании в коде class my_class класс не создается это описание
сначала класс надо проинстанцировать создается класс через
$person = new my_class {}

свойство класса это
переменная только внутри класса
class animal {
public $name;
public $age = 0;
}

$cat = new animal();
$dog = new animal;

$cat->name = 'murzik';
$dog->name = 'tuzik';

echo $cat->name;

у животного есть поведение
описывается поведение методами класса или обьекта
метод класса = функция только внутри класса 

class animal {
public $name;
public $age = 0;
function sayHello(){
 echo "Hello";
  }
}

вызвать метод  say hello класса cat
$cat->sayHello();
-------------------------------------------------------------
class animal {
public $name;
public $age = 0;
function sayHello($word){
 echo "Hello";
  }
}

$cat ->sayHello('meow');
$dog ->sayHello('gav');
 ----------------------------------
!!!!! обращение из методов к свойствам только через this !!!!
ссылка на тот обьект который вызвал этот метод
this  это тот кто вызвал

class animal {
public $name;
public $age = 0;
function sayHello($word){
 echo $this->name. ' say ' .$word;
  }
 function drawBr(){
echo '<br>'
}

!!! обращение внутри метода к другому методу этого же класса тоже через this !!!!!!!!

class animal {
public $name;
public $age = 0;
function sayHello($word){
 echo $this->name. ' say ' .$word;
 this->drawBr();
  }
 function drawBr(){
echo '<br>'
  }
}

псевдоконстанты
__METHOD__
__CLASS__ 

конструктор это метод автоматически вызывается при создании обьекта
если нужно описать конструктор то нужно писать
магические методы
function __construct($num){
echo "object #num  created" }
очень удобен дя инициализации

деструктор не принимается параметров
function __destruct() {
 echo "object #num  created" }
метод который дергается при удалении обьектов 
очередность удаления не определена
удалением обьектов занимается сборщик мусора
из деструктора не обращатся к другим обьектам
 
также конструктор можно вызвать как
 одноименный метод  с именем класса
то есть конструктор класса my class может быть вызвать чезе отдноименный метод function my class

когда удаляется обьект в пхп
- когда не ведет ниодна ссылка

скопировать обьект
$bigcat = clone $cat;
конструктор вызывается только при создании а не при копировании



понедельник, 28 апреля 2014 г.

Simple API with Nginx and PostgreSQL

Simple API with Nginx and PostgreSQL

26 Jul 2013
How to build a simple REST API using only Nginx and PostgreSQL.
Sometimes it’s overkill to use a web framework if you only need to develop a very simple REST API. It turns out that Nginx can be used to develop a full fledged REST API and PostgreSQL can easily be used for persistence.
In this blog post I’m going to show you how to create a simple CRUD API for articles.

Setup

I recommend that you use the OpenResty to install Nginx. It contains the standard Nginx core and lots of 3rd-party Nginx modules including the Postgres upstream module that allows Nginx to communicate with a PostgreSQL database. OpenResty is not an Nginx fork, just a software bundle so there’s nothing to worry about.
This is how I installed and compiled OpenResty on my Mac:
brew install pcre

tar xzvf ngx_openresty-1.4.1.1.tar.gz

cd ngx_openresty-1.4.1.1/

./configure \
--with-cc-opt="-I/usr/local/Cellar/pcre/8.33/include" \
--with-ld-opt="-L/usr/local/Cellar/pcre/8.33/lib" \
--with-http_postgres_module
Remember to change pcre version number to the one you have installed. It might differ.
For this blog post I used PostgreSQL 9.2. You can install PostgreSQL from Homebrew or usePostgres.app .

Create the database

CREATE DATABASE articledb WITH OWNER username ENCODING 'UTF8';

CREATE TABLE articles (
 id serial PRIMARY KEY,
  title varchar(50) NOT NULL,
  body varchar(32000) NOT NULL,
  created_at timestamp DEFAULT current_timestamp
);

# add a few rows:
INSERT INTO articles (title, body) VALUES ('Test title 1', 'Test body 1');
INSERT INTO articles (title, body) VALUES ('Test title 2', 'Test body 2');
INSERT INTO articles (title, body) VALUES ('Test title 3', 'Test body 3');

The complete nginx.conf

worker_processes 8;

events {}

http {
  upstream database {
    postgres_server 127.0.0.1 dbname=articledb user=username password=yourpass;
  }
  
  server {
    listen       8080;
    server_name  localhost;

    location /articles {
      postgres_pass database;
      rds_json on;
      postgres_query    HEAD GET  "SELECT * FROM articles";
      
      postgres_escape $title $arg_title;
      postgres_escape $body  $arg_body;
      postgres_query
        POST "INSERT INTO articles (title, body) VALUES($title, $body) RETURNING *";
      postgres_rewrite  POST changes 201;
    }

    location ~ /articles/(?<id>\d+) {
      postgres_pass database;
      rds_json  on;
      postgres_escape $escaped_id $id;
      postgres_query    HEAD GET  "SELECT * FROM articles WHERE id=$escaped_id";
      postgres_rewrite  HEAD GET  no_rows 410;

      postgres_escape $title $arg_title;
      postgres_escape $body  $arg_body;
      postgres_query
        PUT "UPDATE articles SET title=$title, body=$body WHERE id=$escaped_id RETURNING *";
      postgres_rewrite  PUT no_changes 410;

      postgres_query    DELETE  "DELETE FROM articles WHERE id=$escaped_id";
      postgres_rewrite  DELETE  no_changes 410;
      postgres_rewrite  DELETE  changes 204;
    }
  }
}

Test drive the API

Get all articles:
curl http://localhost:8080/articles
Create a new article:
curl -X POST http://localhost:8080/articles?title=Article1&body=body1
Update article:
curl -X PUT http://localhost:8080/articles/1?title=Article2&body=body2
Delete article:
curl -X DELETE http://localhost:8080/articles/1

понедельник, 21 апреля 2014 г.

службы для пхп


http://php.ru/manual/refs.remote.other.html

Вернуться к: Справочник функций

    AMQP
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        AMQPConnection — Класс AMQPConnection
        AMQPExchange — Класс AMQPExchange
        AMQPQueue — Класс AMQPQueue
    chdb — Constant hash database
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        chdb — The chdb class
        chdb Функции
    cURL — Клиентская библиотека работы с URL
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        cURL
    FAM — File Alteration Monitor
        Введение
        Установка и настройка
        Предопределенные константы
        FAM Функции
    FTP
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        FTP Функции
    Gearman
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        GearmanClient — Класс GearmanClient
        GearmanJob — Класс GearmanJob
        GearmanTask — Класс GearmanTask
        GearmanWorker — Класс GearmanWorker
    Gopher — Net Gopher
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        Gopher Функции
    Gupnp
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        Gupnp Функции
    HTTP
        Введение
        Установка и настройка
        Предопределенные константы
        Request Options — Options usable with the HttpRequest class and request functions
        HttpDeflateStream — The HttpDeflateStream class
        HttpInflateStream — The HttpInflateStream class
        HttpMessage — The HttpMessage class
        HttpQueryString — The HttpQueryString class
        HttpRequest — The HttpRequest
        HttpRequestPool — The HttpRequestPool class
        HttpResponse — The HttpResponse
        HTTP Функции
    Hyperwave
        Введение
        Установка и настройка
        Предопределенные константы
        Integration with Apache
        Hyperwave Функции
    Hyperwave API
        Введение
        Установка и настройка
        Предопределенные константы
        Hyperwave API Функции
    Java — PHP / Java Integration
        Введение
        Установка и настройка
        Предопределенные константы
        Java Servlet SAPI
        Примеры
        Java Функции
    LDAP — Облегчённый протокол доступа к каталогам (LDAP)
        Введение
        Установка и настройка
        Предопределенные константы
        Использование вызовов PHP LDAP
        Примеры
        LDAP
    Lotus Notes
        Введение
        Установка и настройка
        Предопределенные константы
        Lotus Notes Функции
    Memcache
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        Memcache — Класс Memcache
        Memcache Функции
    Memcached
        Введение
        Установка и настройка
        Предопределенные константы
        Время хранения объекта
        Callbacks
        Sessions support
        Memcached — The Memcached class
    mqseries
        Введение
        Установка и настройка
        Предопределенные константы
        mqseries Функции
    Network
        Введение
        Установка и настройка
        Предопределенные константы
        Сетевые Функции
    RRD — RRDtool
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        RRD Функции
        RRDCreator — The RRDCreator class
        RRDGraph — The RRDGraph class
        RRDUpdater — The RRDUpdater class
    SAM — Simple Asynchronous Messaging
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        SAM Функции
    SNMP
        Введение
        Установка и настройка
        Предопределенные константы
        SNMP Функции
        SNMP — The SNMP class
        SNMPException — The SNMPException class
    Sockets
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        Socket Errors
        Socket Функции
    SSH2 — Secure Shell2
        Введение
        Установка и настройка
        Предопределенные константы
        SSH2 Функции
    Stomp — Stomp Client
        Введение
        Установка и настройка
        Примеры
        Stomp
        Stomp — Класс Stomp
        StompFrame — Класс StompFrame
        StompException — Класс StompException
    SVM — Support Vector Machine
        Введение
        Установка и настройка
        Примеры
        SVM — The SVM class
        SVMModel — The SVMModel class
    SVN — Subversion
        Введение
        Установка и настройка
        Предопределенные константы
        SVN
    TCP — TCP Wrappers
        Введение
        Установка и настройка
        Предопределенные константы
        TCP Функции
    Varnish
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        VarnishAdmin — The VarnishAdmin class
        VarnishStat — The VarnishStat class
        VarnishLog — The VarnishLog class
    YAZ
        Введение
        Установка и настройка
        Предопределенные константы
        Примеры
        YAZ Функции
    YP/NIS
        Введение
        Установка и настройка
        Предопределенные константы
        YP/NIS Функции



Вернуться к: Справочник функций

гирман дока




    Введение
    Установка и настройка
        Требования
        Установка
        Настройка во время выполнения
        Типы ресурсов
    Предопределенные константы
    Примеры
        Basic usage
        Основные клиент и обработчик Gearman, фоновый режим
        Базовые клиент и обработчик Gearman , передающие задачи
    GearmanClient — Класс GearmanClient
        GearmanClient::addOptions — Добавить клиентские опции
        GearmanClient::addServer — Добавить сервер задач для клиента
        GearmanClient::addServers — Добавить список серверов задач для клиента
        GearmanClient::addTask — Добавить задачу, которая будет выполнена в параллельном режиме
        GearmanClient::addTaskBackground — Добавить фоновую задачу для работы в параллельном режиме
        GearmanClient::addTaskHigh — Добавить высокоприоритетную задачу для работы в параллельном режиме
        GearmanClient::addTaskHighBackground — Добавить высокоприоритетную фоновую задачу для работы в параллельном режиме
        GearmanClient::addTaskLow — Добавить низкоприоритетную задачу для работы в параллельном режиме
        GearmanClient::addTaskLowBackground — Добавить низкоприоритетную фоновую задачу для работы в параллельном режиме
        GearmanClient::addTaskStatus — Добавить задачу для получения статуса
        GearmanClient::clearCallbacks — Очистить все функции обратного вызова данной задачи
        GearmanClient::clone — Создать копию объекта GearmanClient
        GearmanClient::__construct — Создать экземпляр GearmanClient
        GearmanClient::context — Возвращает контекст приложения
        GearmanClient::data — Возвращает данные приложения (функция устарела)
        GearmanClient::do — Выполняет одну задачу и возвращает результат [Устаревший метод]
        GearmanClient::doBackground — Запускает выполнение задачи в фоновом режиме
        GearmanClient::doHigh — Запускает на выполнение задачу с высоким приоритетом
        GearmanClient::doHighBackground — Запускает на выполнение с высоким приоритетом задачу в фоновом режиме
        GearmanClient::doJobHandle — Получить дескриптор выполняющейся задачи
        GearmanClient::doLow — Запускает на выполнение задачу с низким приоритетом
        GearmanClient::doLowBackground — Запускает на выполнение с низким приоритетом задачу в фоновом режиме
        GearmanClient::doNormal — Run a single task and return a result
        GearmanClient::doStatus — Get the status for the running task
        GearmanClient::echo — Отправляет данные всем серверам заданий, чтобы проверить отклик [Устаревший метод]
        GearmanClient::error — Вернуть строку ошибки для последней встретившейся ошибки
        GearmanClient::getErrno — Получить значение errno
        GearmanClient::jobStatus — Get the status of a background job
        GearmanClient::ping — Send data to all job servers to see if they echo it back
        GearmanClient::removeOptions — Удалить клиентские опции
        GearmanClient::returnCode — Получить последний возвращённый код Gearman
        GearmanClient::runTasks — Запустить список задач в параллельном режиме
        GearmanClient::setClientCallback — Установить функцию обратного вызова, когда есть пакет данных для задачи (устаревший метод)
        GearmanClient::setCompleteCallback — Установите функцию, которая будет вызвана по завершении задачи
        GearmanClient::setContext — Установить данные приложения
        GearmanClient::setCreatedCallback — Установить функцию обратного вызова, когда задача ставится в очередь
        GearmanClient::setData — Установить данные приложения (устаревший метод)
        GearmanClient::setDataCallback — Callback function when there is a data packet for a task
        GearmanClient::setExceptionCallback — Set a callback for worker exceptions
        GearmanClient::setFailCallback — Set callback for job failure
        GearmanClient::setOptions — Set client options
        GearmanClient::setStatusCallback — Set a callback for collecting task status
        GearmanClient::setTimeout — Set socket I/O activity timeout
        GearmanClient::setWarningCallback — Set a callback for worker warnings
        GearmanClient::setWorkloadCallback — Set a callback for accepting incremental data updates
        GearmanClient::timeout — Get current socket I/O activity timeout value
    GearmanJob — Класс GearmanJob
        GearmanJob::complete — Send the result and complete status (deprecated)
        GearmanJob::__construct — Create a GearmanJob instance
        GearmanJob::data — Send data for a running job (deprecated)
        GearmanJob::exception — Send exception for running job (deprecated)
        GearmanJob::fail — Send fail status (deprecated)
        GearmanJob::functionName — Get function name
        GearmanJob::handle — Get the job handle
        GearmanJob::returnCode — Get last return code
        GearmanJob::sendComplete — Send the result and complete status
        GearmanJob::sendData — Send data for a running job
        GearmanJob::sendException — Send exception for running job (exception)
        GearmanJob::sendFail — Send fail status
        GearmanJob::sendStatus — Send status
        GearmanJob::sendWarning — Send a warning
        GearmanJob::setReturn — Set a return value
        GearmanJob::status — Send status (deprecated)
        GearmanJob::unique — Get the unique identifier
        GearmanJob::warning — Send a warning (deprecated)
        GearmanJob::workload — Get workload
        GearmanJob::workloadSize — Get size of work load
    GearmanTask — Класс GearmanTask
        GearmanTask::__construct — Create a GearmanTask instance
        GearmanTask::create — Create a task (deprecated)
        GearmanTask::data — Get data returned for a task
        GearmanTask::dataSize — Get the size of returned data
        GearmanTask::function — Get associated function name (deprecated)
        GearmanTask::functionName — Get associated function name
        GearmanTask::isKnown — Determine if task is known
        GearmanTask::isRunning — Test whether the task is currently running
        GearmanTask::jobHandle — Get the job handle
        GearmanTask::recvData — Read work or result data into a buffer for a task
        GearmanTask::returnCode — Get the last return code
        GearmanTask::sendData — Send data for a task (deprecated)
        GearmanTask::sendWorkload — Send data for a task
        GearmanTask::taskDenominator — Get completion percentage denominator
        GearmanTask::taskNumerator — Get completion percentage numerator
        GearmanTask::unique — Get the unique identifier for a task
        GearmanTask::uuid — Get the unique identifier for a task (deprecated)
    GearmanWorker — Класс GearmanWorker
        GearmanWorker::addFunction — Register and add callback function
        GearmanWorker::addOptions — Add worker options
        GearmanWorker::addServer — Add a job server
        GearmanWorker::addServers — Add job servers
        GearmanWorker::clone — Create a copy of the worker
        GearmanWorker::__construct — Create a GearmanWorker instance
        GearmanWorker::echo — Test job server response
        GearmanWorker::error — Get the last error encountered
        GearmanWorker::getErrno — Get errno
        GearmanWorker::options — Get worker options
        GearmanWorker::register — Register a function with the job server
        GearmanWorker::removeOptions — Remove worker options
        GearmanWorker::returnCode — Get last Gearman return code
        GearmanWorker::setOptions — Set worker options
        GearmanWorker::setTimeout — Set socket I/O activity timeout
        GearmanWorker::timeout — Get socket I/O activity timeout
        GearmanWorker::unregister — Unregister a function name with the job servers
        GearmanWorker::unregisterAll — Unregister all function names with the job servers
        GearmanWorker::wait — Wait for activity from one of the job servers
        GearmanWorker::work — Wait for and perform jobs



Вернуться к: Другие службы

Настройка автоматического удаления сессий сборщиком мусора (garbage collection) в PHP
Post on 25.12.2013 by root

Для автоматического удаления файлов сессии, необходимо настроить сборщика мусора.

Сборщик мусора запускается с вероятностью рассчитанной по формуле:
gc_probability/gc_divisor для сессий существующих дольше чем gc_maxlifetime секунд
где gc_probability, gc_divisor, gc_maxlifetime настройки из конфигурационного файла php.ini

Пример:

gc_probability = 1
gc_divisor = 100
gc_maxlifetime = 60

Сборщик мусора удалит сессии старше 60 секунд с вероятностью 1%

Тестирование проводилось на php версии 5.3.10-1ubuntu3.8

 Утечки памяти в PHP

12 сентября 2010

Утечки памяти обычно не беспокоят PHP-разработчиков. Типичное приложение обрабатывает один запрос и работает не больше секунды. После этого вся использованная им память освобождается. Даже если приложение кушает слишком много, максимум, разработчик упирается в memory_limit, выставленный хостером, что решить в общем случае довольно просто: как только переменная становится не нужна, очищаем память, занимаемую ей, при помощи unset.

Однако, при выполнении ресурсоёмких задач (например, обработки большого количества данных) или запуске PHP как демона проблема утечек встаёт очень остро.

В PHP 5.2 нет полноценного сборщика мусора. Вместо него используется подсчёт ссылок.

Все значения переменных хранятся в памяти. И чтобы занимать как можно меньше места, переменные с одинаковыми значениями просто ссылаются на одну и ту же область памяти. При этом количество ссылок подсчитывается и, как только оно становится равно нулю, память освобождается.

$a = 10; // выделяем область в памяти, одна ссылка
$b = $a; // две ссылки
unset($a); // одна ссылка

$a = 10; // выделяем область в памяти, одна ссылка
$b = $a; // две ссылки
$b = 1; // выделяем вторую область в памяти под значение 1, одна ссылка на 1, одна ссылка на 10

В PHP 5.2 причиной утечек являются циклические ссылки:

class A {
  private $b;

  function __construct(){
    $this->b = new B($this);
  }
}

class B {
  private $a;

  function __construct($a){
    $this->a = $a;
  }
}

$i=1;
while($i<=1000){
  $a = new A();
  // 1 ссылка на A ($a).
  // 1 ссылка на B (A::$b).
  // 2 ссылки на A (B::$a).
  unset($a);
  // 1 ссылка на A всё ещё осталась. Память освобождать рановато.
  echo $i."\t".memory_get_usage()."\n";
  $i++;
}

Исправляется это явным уничтожением ссылки на B при помощи unset:

class A {
  private $b;

  function __construct(){
    $this->b = new B($this);
  }

  function __destruct(){
    unset($this->b);
  }
}

class B {
  private $a;

  function __construct($a){
    $this->a = $a;
  }
}

$i=1;
while($i<=1000){
  $a = new A();
  // 1 ссылка на A ($a).
  // 1 ссылка на B (A::$b).
  // 2 ссылки на A (B::$a).
  unset($a);
  // 1 ссылка на A (минус одна в B::__destruct).
  // 0 ссылок на A (unset). Память можно освободить.
  echo $i."\t".memory_get_usage()."\n";
  $i++;
}

В PHP 5.3 более умный сборщик мусора, который умеет находить и подчищать последствия использования циклических ссылок. Однако, поиск таких ссылок занимает значительное время и зависит от количества «неподчищенных» ссылок. Плюс к этому работает сборщик не постоянно, а срабатывает только при наполнении буфера ссылок. То есть до его срабатывания какое-то количество памяти всё-таки успевает утекать.

На заметку. Посмотреть, сколько памяти кушает ваше приложение можно при помощи следующих функций:

    memory_get_usage() — использованная скриптом память в байтах в момент вызова функции.
    memory_get_usage(true) — использованная скриптом и менеджером памяти PHP память в байтах в момент вызова функции.
    memory_get_peak_usage() — максимальное количество памяти в байтах, использованной скриптом с запуска скрипта до момента вызова функции.
    memory_get_peak_usage(true) — максимальное количество памяти в байтах, использованной скриптом и менеджером памяти PHP с запуска скрипта до момента вызова функции.

Сборщик мусора в PHP 5


Сборщик мусора в PHP 5
18 марта 2008, обновлена 09 августа 2012
PHP

Предупреждение! Начиная с PHP 5.3, сборщик мусора удаляет циклические ссылки. Статья сохранена для истории.

Сегодня на работе разбирался с PHPшным сборщиком мусора. Обнаружилась одна жутко неприятная вещь, которая называется recursive reference memory leak – объекты с перекрестными ссылками не удаляются из памяти.

Как известно, объекты PHP 5 реализуются через механизм smart-pointer. Понимание этого механизма вообще нужно для адекватной работы с объектами, так что изложу вкратце. Осторожно, мнение выходца из C++. :)

Что такое smart-pointer? Это такой паттерн, который подсчитывает количество ссылок на себя, и когда оно становится равным нулю, удаляет объект, на который ссылается сам.

Подробнее про самостоятельную реализацию smart-pointer на PHP можно почитать, например, в Advanced PHP Programming.

    Когда создается объект, под него выделяется участок память и smart-pointer. Переменной присваивается ссылка на smart-pointer.
    При копировании объекта, на самом деле, создается еще одна ссылка на smart-pointer.
    При клонировании объекта оператором clone создается копия памяти и новый smart-pointer с одной ссылкой (той, что новая).
    При выполнении unset($object) удаляется только данная ссылка на объект – если это не последняя ссылка, то объект остается в памяти. (важно!)
    При выполнении $object->__destruct() выполняется деструктор объекта как метод – это тоже не освобождает память! Так же, как и object.~Class() в C++. Более того, это, возможно, испортит объект – а ссылки на него могли остаться.
    Единственный способ удалить объект из памяти – удалить все ссылки на него.

Конечно, обычно PHP очищает ссылки при выходе из области определения переменной, и память освобождается вовремя.

Поправка: память очистится не сразу, а при ближайшем запуске сборщика мусора.

Но есть одна ситуация, и довольно часто используемая, когда этого не происходит.

class A {
  function __construct () {
    $this->b = new B($this);
  }
}

class B {
  function __construct ($parent = NULL) {
    $this->parent = $parent;
  }
}

while (true) {
  $a = new A();
}

Удаление свойства $a->b происходит после удаления объекта $a (резонно – в деструкторе оно может пригодиться). В деструкторе B объект `$b->parent уже удален, и эта ссылка не очищается. Отсюда утечка памяти.
Пример из жизни

Привожу пример из жизни классической ActiveRecord-модели:

class Record{
  #...
  private $_fields;
  #...
}

class Field {
  #...
  private $_record;
  #...
}
...
while ($record = $result->next()) {
  $record->doSomething();
}

Так вот такой код со страшной силой жрет ОЗУ.
Решение

На данный момент приходится обнулять ссылки вручную:

class A {
  function __construct () {
    $this->b = new B($this);
  }
  function __destruct () {
    $this->b->__destruct();
  }
}

class B {
  function __construct ($parent = NULL) {
    $this->parent = $parent;
  }
  function __destruct () {
    unset($this->parent);
  }
}

[edit] кроме того, вызывать деструктор для A придется вручную – поскольку в ссылка на него остается во вложенном объекте, он не будет удален автоматически.

Багрепорту, кстати, уже два с половиной года: [http://bugs.php.net/bug.php?id=33595](http://bugs.php.net/bug.php?id=33595, и его вроде как закрыли в PHP 5.3.

Одно это делает переход на 5.3 весьма ожидаемым.

Работа с памятью (и всё же она есть)


 Работа с памятью (и всё же она есть)
PHP*
Существует распространенное мнение, что «рядовому» PHP разработчику практически не нужно заботиться об управлении памятью, однако «заботиться» и «знать» всё же немного разные понятия. Попытаюсь осветить некоторые аспекты управлению памятью при работе с переменными и массивами, а также интересные «подводные камни» внутренней оптимизации PHP. Как вы сможете убедиться, оптимизация это хорошо, но если не знать как именно она «оптимизирует», то можно столкнуться с «неочевидными граблями», которые могут вас заставить изрядно понервничать.


Общие сведения

Небольшой ликбез

Переменная в PHP как бы состоит из двух частей: "имени", которое хранится в hash_table symbol_table, и "значения", которое хранится в zval контейнере.
Такой механизм позволяет создавать несколько переменных ссылающихся на одно значение, что в отдельных случаях позволяет оптимизировать потребление памяти. О том, как это выглядит на практике будет написано далее.

Наиболее частыми элементами кода, без которых сложно себе представить более менее функциональный скрипт, являются следующие моменты:
— создание, присвоение и удаление переменных (чисел, строк и т.п.),
— создание массивов и их обход (в качестве примера будет использована функция foreach),
— передача и возврат значений для функций/методов.

Именно об этих аспектах работы с памятью и будет последующее описание. Получилось достаточно объемно, но ничего мега-сложного не будет и всё будет достаточно просто, очевидно и с примерами.

Первый пример работы с памятью

Для начала базовый пример того, как будет производиться анализ потребления памяти.
Для этого нам потребуется пара простых функций (файл func.php):

    <?php
    function memoryUsage($usage, $base_memory_usage) {
    printf("Bytes diff: %d\n", $usage - $base_memory_usage);
    }
    function someBigValue() {
    return str_repeat('SOME BIG STRING', 1024);
    }
    ?>



И простой первый пример теста потребления памяти для строки:

    <?php
    include('func.php');
    echo "String memory usage test.\n\n";
    $base_memory_usage = memory_get_usage();
    $base_memory_usage = memory_get_usage();
   
    echo "Start\n";
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    $a = someBigValue();
   
    echo "String value setted\n";
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    unset($a);
   
    echo "String value unsetted\n";
    memoryUsage(memory_get_usage(), $base_memory_usage);
    ?>


Примечание: несомненно код является неоптимизированным с точки зрения работоспособности, но в данном случае нам исключительно важна наглядность потребления памяти, для которой и реализовано данное представление.

Результат кода вполне очевиден:

    String memory usage test.

    Start
    Bytes diff: 0
    String value setted
    Bytes diff: 15448
    String value unsetted
    Bytes diff: 0



Тот же самый пример, но вместо unset($a) используем $a=null;:

    Start
    Bytes diff: 0
    String value setted
    Bytes diff: 15448
    String value set to null
    Bytes diff: 76


Как видите, переменная не была полностью уничтожена. Под нее остается выделенным еще 76 байт.
Достаточно прилично, если учесть, что ровно столько же выделяется и под переменные типа boolean, integer, float. Речь идет не об объеме памяти, выделяемой под значение переменной, а о полном потреблении памяти для хранения сведений о присвоенной переменной (zval контейнер со значением и само имя переменной).
Так что если вы хотите освободить память при помощи присвоения, то не является принципиальным присвоение именно null значения. Выражение $a=10000; даст тот же результат для расхода памяти.

В документации PHP сказано, что приведение к null уничтожит переменную и ее значение, однако, по данному скрипту видно что это не так, что собственно является багом (документации).

Зачем использовать присвоение null, если можно unset()?
Присвоение — это присвоение, (спасибо КО), то есть изменяется значение переменной, соответственно, если новое значение требует меньше памяти, то она высвобождается сразу, однако это требует вычислительных ресурсов (пусть и сравнительно немного).
unset() в свою очередь освобождает память, выделенную под имя переменной и ее значение.
Отдельно стоит упомянуть момент, что unset() и присвоение null совершенно по разному работают со ссылками на переменные. Unset() уничтожит только ссылку, в то время как присвоение null изменит значение, на которое ссылаются имена переменных, соответственно все переменные станут ссылаться на значение null.

Примечание:
Встречается заблуждение, что unset() является функцией, однако, это не верно. unset() — это языковая конструкция (как например if), о чем прямо сказано в документации, соответственно ее нельзя использовать для обращения через значение переменной:

    $unset_func_name = 'unset';
    $unset_func_name($some_var);



Немного дополнительной информации для праздных размышлений (при изменении примера выше):
$a = array();
выделит 164 байта, unset($a) всё вернет.

class A { }
$a = new A();
выделит 184 байта, unset($a) всё вернет.

$a = new stdClass();
выделит 272 байта, но после unset($a) «утекут» 88 байт (куда именно и почему они утекли, мне пока не удалось выяснить).

Пока приведенные примеры не являются критичными в плане потребления памяти, так как строковые и числовые значения достаточно очевидно хранятся и обрабатываются. Всё становится значительно хуже, когда в ход идут массивы (объекты тоже имеют целый ряд особенностей, однако для этого уже потребуется отдельная статья).

Массивы

Массивы в PHP «съедают» достаточно памяти, и именно в них как правило хранят значительные объемы данных при обработке, поэтому следует очень аккуратно относиться к работе с ними. Однако, работа с массивами в PHP имеет свои «прелести оптимизации» и об одном из таких моментов, связанных с потреблением памяти, стоит упомянуть.

Коварный пример №1

    <?php
    include('func.php');
    echo "Array memory usage example.";
    $base_memory_usage = memory_get_usage();
    $base_memory_usage = memory_get_usage();
   
    echo 'Base usage.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    $a = array(someBigValue(), someBigValue(), someBigValue(), someBigValue());
   
    echo 'Array is set.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    foreach ($a as $k=>$v) {
    $a[$k] = someBigValue();
    unset($k, $v);
    echo 'In FOREACH cycle.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
    }
   
    echo 'Usage right after FOREACH.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    unset($a);
    echo 'Array unset.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
    ?>


На первый взгляд может показаться, что потребление памяти массивом $a не будет меняться (за исключением установки переменных $k и $v), однако PHP имеет особенный подход при работе с массивами в этом случае.

Посмотрите на вывод:

    Array memory usage example.Base usage.
    Bytes diff: 0
    Array is set.
    Bytes diff: 61940
    In FOREACH cycle.
    Bytes diff: 77632
    In FOREACH cycle.
    Bytes diff: 93032
    In FOREACH cycle.
    Bytes diff: 108432
    In FOREACH cycle.
    Bytes diff: 123832
    Usage right after FOREACH.
    Bytes diff: 61940
    Array unset.
    Bytes diff: 0


Получается, что в последней итерации цикла foreach в данном случае потребление массивом памяти возросло в два раза, хотя по самому коду это не очевидно. Но сразу после цикла, потребление памяти вернулось к прежнему значению. Чудеса да и только.
Причиной тому является оптимизация использования массива в цикле. На время работы цикла, при попытке изменить исходный массив, неявно создается копия структуры массива (но не копия значений), которая и становится доступной по завершению цикла, а исходная структура уничтожается. Таким образом, в вышеприведенном примере, если вы присваиваете новые значения исходному массиву, то они не будут заменены сразу, а для них будет выделена отдельная память, которая будет возвращена по выходу из цикла.
Этот момент очень легко пропустить, что может привести к значительному потреблению памяти на время работы цикла с большими массивами данных, например при выборке из БД.

Примечание:
Внутри самого цикла, уже после изменения значения $a[$k], вы не сможете получить значение которое всё еще хранится в исходном массиве если не сохранили значение $v. Повторное обращение к $a[$k] выдаст уже новое значение.

Дополнение от пользователя zibada (в кратце):
Важно учесть, что выделение памяти под новый «временный массив» в случае внесения изменений, произойдет единовременно для всей структуры массива, но отдельно для каждого изменяемого элемента. Таким образом, если имеется массив с большим количеством элементов, (но не обязательно с большими значениями), то единовременное потребление памяти при таком копировании будет существенно.

Коварный пример №2
Чуть-чуть изменим код.

    echo 'Array is set.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
    $b = &$a; // Добавим это
    foreach ($a as $k=>$v) {
    $a[$k] = someBigValue();
    unset($k, $v);
    echo 'In FOREACH cycle.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
    }
    unset($b); // И это
    echo 'Usage right after FOREACH.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);



Сам код цикла мы никак не меняли, единственное что мы изменили, это увеличили счетчик ссылок на исходный массив, но это в корне поменяло работу цикла:

    Bytes diff: 0
    Array is set.
    Bytes diff: 61940
    In FOREACH cycle.
    Bytes diff: 61988
    In FOREACH cycle.
    Bytes diff: 61988
    In FOREACH cycle.
    Bytes diff: 61988
    In FOREACH cycle.
    Bytes diff: 61988
    Usage right after FOREACH.
    Bytes diff: 61940
    Array unset.
    Bytes diff: 0


Небольшое изменение: (61988 — 61940 = 48 байт на хранение переменной-ссылки $b).
В остальном же мы видим, что если массив, используемый для цикла, имеет больше чем одну ссылку на себя, тогда для него не применяется оптимизация из примера №1, т.е. для присвоения используется оригинальный массив.
Точно такой же результат мы получим, если используем для цикла массив $b или же используем в цикле передачу значения по ссылке:

    echo 'Array is set.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    foreach ($a as $k=>&$v) {
    $a[$k] = someBigValue(); // Или $v = someBigValue();
    unset($k, $v);
    echo 'In FOREACH cycle.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
    }
   
    echo 'Usage right after FOREACH.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);



Результат:

    Bytes diff: 0
    Array is set.
    Bytes diff: 61940
    In FOREACH cycle.
    Bytes diff: 61940
    In FOREACH cycle.
    Bytes diff: 61940
    In FOREACH cycle.
    Bytes diff: 61940
    In FOREACH cycle.
    Bytes diff: 61940
    Usage right after FOREACH.
    Bytes diff: 61940
    Array unset.
    Bytes diff: 0


Здесь стоит отдельно отметить, что добавление передачи $v по ссылке хоть и не увеличивает счетчик ссылок исходного массива, но тоже приводит к отключению «оптимизации».

Передача по ссылке или передача через копирование


Рассмотрим случай, «что делать» если требуется передать в метод или функцию (или вернуть из них), какое-либо очень большое значение. Первым очевидным решением обычно рассматривают использование передачи/возвращения по ссылке.
Однако в документации по PHP сказано: Не используйте возврат по ссылке для увеличения производительности. Ядро PHP само занимается оптимизацией.
Попытаемся разобраться в том, что же это за «оптимизация».

Для начала самый простой пример (пока без передачи аргументов):

    …
    $a = someBigValue();
    $b = $a;
   
    echo "String value setted";
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    unset($a, $b);
    ...


По «прямой логике», в памяти должно выделиться два блока под значение переменных. Однако PHP оптимизирует этот момент:

    Start
    Bytes diff: 0
    String value setted
    Bytes diff: 15496
    String value unsetted
    Bytes diff: 0


В данном случае 15448 байт занимается переменная $a, остальные же 48 байт выделены под переменную $b, хотя между ними и не установлена связь по ссылке. Данное потребление памяти сохраняется до тех пор, пока мы как-либо не изменим одну из этих переменных, а точнее сказать вообще что-либо не сделаем с ее значением, даже если мы его не меняем по факту:

    $a = someBigValue();
    $b = $a;
    $b = strval($b);
   
    echo "String value setted";
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    unset($a, $b);



В результате получим вывод:

    Bytes diff: 0
    String value setted
    Bytes diff: 30896
    String value unsetted
    Bytes diff: 0


Как мы видим, попытка «тронуть» значение переменной $b приводит к тому, что теперь скрипт выделяет для ее хранения отдельную область памяти. То же самое произойдет если мы попытаемся «тронуть» значение $a.

Данная оптимизация действует для конкретных значений, коими также являются и отдельные значения массива.
Чтобы это лучше понять, взглянем на пример ниже:

    $a = array(someBigValue(), someBigValue()); // 31052 байта
    $b = $a; // + 48 байт = 31100 байта
    $b[0] = someBigValue();
   
    echo "String value setted";
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    unset($a, $b);



Данный пример даст выход:

    Bytes diff: 0
    String value setted
    Bytes diff: 46704
    String value unsetted
    Bytes diff: 0


То есть в результате новая память (15к+ байт) была выделена для создания только копии значения для нулевого элемента массива, а не для всего массива $b. Значение $b[1] всё еще «оптимизированно связано» с $a[1].

Всё выше описанное действует аналогично и для передачи/возврата значений через «оптимизированное копирование» внутрь/из функций и методов. Если внутри метода вы никак не «трогаете» переданное значение, то для него не будет выделена отдельная область памяти (память будет выделена только под имя переменной, чтобы связать ее со значением). Если же вы передаете «через копирование» и изменяете значение внутри метода, то перед попыткой сделать изменение уже будет создана действительная полная копия значения.

Таким образом PHP действительно избавляет от необходимости использовать передачу по ссылке для оптимизации использования памяти. Передача по ссылке имеет практическое значение только если исходное значение требуется изменить с отображением этих изменений извне метода.

Код для примера:

    <?php
    include('func.php');
   
    function testUsageInside($big_value, $base_memory_usage) {
    echo 'Usage inside function then $big_value NOT changed.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    $big_value[0] = someBigValue();
    echo 'Usage inside function then $big_value[0] changed.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    $big_value[1] = someBigValue();
    echo 'Usage inside function then also $big_value[1] changed.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    }
   
    echo "Array memory usage example.";
    $base_memory_usage = memory_get_usage();
    $base_memory_usage = memory_get_usage();
   
    echo 'Base usage.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    $a = array(someBigValue(), someBigValue(), someBigValue(), someBigValue());
   
    echo 'Array is set.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    testUsageInside($a, $base_memory_usage);
   
    echo 'Usage right after function call.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
   
    unset($a);
    echo 'Array unset.'.PHP_EOL;
    memoryUsage(memory_get_usage(), $base_memory_usage);
    ?>



Вывод:

    Array memory usage example.
    Base usage.
    Bytes diff: 0
    Array is set.
    Bytes diff: 61940
    Usage inside function then $big_value NOT changed.
    Bytes diff: 61940
    Usage inside function then $big_value[0] changed.
    Bytes diff: 77632
    Usage inside function then also $big_value[1] changed.
    Bytes diff: 93032
    Usage right after function call.
    Bytes diff: 61940
    Array unset.
    Bytes diff: 0


Как видно из примера, в функции не была создана копия массива, несмотря на то, что фактически идет передача значения через копирование. И даже частичная модификация переданного массива не создала полноценную копию, а выделила память только под новые значения.

Исключительно в познавательных целях, стоит обратить внимание на эти два значения:

    Array is set.
    Bytes diff: 61940
    Usage inside function then $big_value NOT changed.
    Bytes diff: 61940


Потребление памяти не увеличилось при передаче управления в функцию, хотя по сути появилась новая переменная $big_value. Это связано с тем, что еще на стадии разбора текста скрипта интерпретатор определил будет ли эта функция использована в коде и заранее выделил для имен ее входных параметров место в памяти (если функция не используется, то интерпретатор ее игнорирует и не выделяет под нее память). А так как имеет место «оптимизированная передача через копирование», то уже существующее имя переменной $big_value было просто неявно «связано» с большим массивом $a. В результате было передано значение в функцию «через копирование» не потратив ни единого дополнительного байта.

Примечание:
В PHP5 (в отличие от PHP4), все объекты по-умолчанию передаются по ссылке, хотя по факту, это неполноценная ссылка. См. эту статью.

Краткие выводы

Несомненно приведенные примеры оптимизации использования памяти в PHP лишь «капля в море», однако они описывают самые частые случаи, когда имеет смысл задуматься о том, какой код выбрать чтобы оптимизировать расход памяти и избавить себя от лишней головной боли.

Отдельно стоило бы затронуть механизм расходования и оптимизации памяти при использовании объектов, однако ввиду обилия возможных примеров этот момент требует отдельной статьи. Возможно когда-нибудь.

PS: Можно было бы разбить это на несколько статей, но не вижу в этом смысла, так как подобную информацию лучше всё же хранить «вместе». Полагаю тем, кому данная информация несет практический смысл, так будет удобнее. Тестировалось на PHP 5.3.2 (Ubuntu 32bit), так что ваши значения по выделенным байтам могут отличаться.

Еще много полезного, но на английском:
nikic.github.com/2011/12/12/How-big-are-PHP-arrays-really-Hint-BIG.html
nikic.github.com/2011/11/11/PHP-Internals-When-does-foreach-copy.html
blog.golemon.com/2007/01/youre-being-lied-to.html
hengrui-li.blogspot.com/2011/08/php-copy-on-write-how-php-manages.html
sldn.softlayer.com/blog/dmcaloon/PHP-Memory-Management-Foreach
blog.preinheimer.com/index.php?/archives/354-Memory-usage-in-PHP.html
derickrethans.nl/talks/phparch-php-variables-article.pdf

UPD
В основной части статьи не был освещен важный момент.
Если есть переменная на которую создана ссылка, то при ее передаче в функцию в качестве аргумента она будет скопирована сразу, то есть не будет применена copy-on-write оптимизация.
Пример:

    <?php
    include('func.php');
    function testFunc($a, $base_memory_usage) {
    memoryUsage(memory_get_usage(), $base_memory_usage);
    }
    $base_memory_usage = 0;
    $base_memory_usage = memory_get_usage();
    memoryUsage(memory_get_usage(), $base_memory_usage); // 0 bytes
    $a = someBigValue();
    $b = &$a;
    memoryUsage(memory_get_usage(), $base_memory_usage); // 15496 bytes
    testFunc($a, $base_memory_usage); // 30896 bytes
    memoryUsage(memory_get_usage(), $base_memory_usage); // 15496 bytes
    unset($a, $b);
    memoryUsage(memory_get_usage(), $base_memory_usage); // 0 bytes
    ?>
   

    потребление памяти
    , массивы
    , строки
    , оптимизация