Применение PHP в облачных вычислениях: Часть 1. Работа с Amazon S3 при помощи Zend Framework

Использование Zend Framework для перемещения данных в облако и из него

Среда Zend Framework содержит несколько классов, которые облегчают использование услуг хранения данных на базе облака. В этой статье показано, как использовать эти классы со службой хранения данных Amazon S3.

Даг Тидуэл (Doug Tidwell), идеолог XML, EMC

Старший Программист Даг Тидуэл является в IBM ведущим авторитетом по Web-сервисам. Он был докладчиком на первой конференции по XML в 1997 году и более десятилетия работал с языками разметки. Он получил степень бакалавра английского языка в Университете Джорджии и степень магистра компьютерных наук в Университете Вандербильда. Его адрес: dtidwell@us.ibm.com. Вы можете также увидеть его Web-страницу на ibm.com/developerWorks/speakers/dtidwell/.


developerWorks Contributing author
        level

12.05.2010

Концепция облачных вычислений обещает неограниченное дисковое пространство для пользователей и приложений. В идеальном мире доступ к этим ресурсам должен осуществляться столь же легко, как к локальному жесткому диску. К сожалению, основные API большинства облачных служб хранения данных вынуждают программистов думать о протоколах и деталях конфигурации, вместо того чтобы просто работать со своими данными. В этой статье рассматриваются классы среды Zend Framework, которые упрощают использование службы хранения данных Amazon S3 в качестве безграничного жесткого диска.

Форматы передачи данных и облачные вычисления

Одна из главных задач при создании приложений, работающих в облаке, - это интерфейс с самой службой. Большинство таких служб предлагают интерфейс на базе REST или SOAP. (S3 обеспечивает тот и другой.) Одно из главных преимуществ REST- или SOAP-интерфейса — отсутствие зависимости от конкретного языка. Это означает, что службу можно вызвать на любом предпочтительном для вас языке программирования. Недостатком является то, что при использовании REST или SOAP необходимо думать не о данных, с которыми работаешь, а о деталях запроса. Например, все запросы к S3 должны содержать жетон аутентификации, состоящий из ключа доступа Amazon и подписи. Это означает, что в запросе должно присутствовать значение типа 0PN5J17HBGZHT7JJ3X82:frJIUN8DYpKDtOLCwo//yllqDzg=.

Очевидно, что более высокоуровневый подход, который позволит сконцентрироваться на данных, вместо подписей и других деталей, значительно улучшил бы эффективность. Именно здесь приходит на помощь класс Zend_Service_Amazon_S3. Этот класс позволяет сосредоточиться на данных, а не на механике заголовков HTTP, конвертов SOAP и других несущественных деталях.


Отправная точка

Если у вас не установлена среда Zend Framework, загрузите и установите полный пакет из zend.com/community/downloads. При этом на компьютер установится Zend Framework, PHP и Web-сервер Apache. После завершения установки зайдите на http://localhost/ZendServer/. За подробностями обращайтесь к инструкции по установке Zend Framework. Если вам удалось войти в консоль ZendServer, значит, все готово.

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

Листинг 1. Хранение верительных данных в коде РНР
// Верительные данные Amazon - Не делайте этого!

$awsKey = "0123456789ABCDEFGHIJ";
$awsSecret = "0123456789abcdefghiABCDEFGHI1234567890AB";

Такой подход допустим, но этот код придется включать в каждый файл PHP, где он нужен. Лучше поместить эти значения в файл PHP.ini, который выглядит как в листинге 2.

Листинг 2. Хранение верительных данных в файле РНР.ini
; Файл конфигурации для хранения секретных ключей, номеров счетов и других полезных
; строк для Amazon и прочих облачных сервисов.

[amazon]
accessKey=0123456789ABCDEFGHIJ
secretKey=0123456789abcdefghiABCDEFGHI1234567890AB
ownerId=123456789012

[nirvanix]
username=jane_doe
password=XXXXXXXX
appKey=01234567-89ab-cdef-0123-456789abcdef

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

Листинг 3. Простой класс PHP для извлечения верительных данных
<?php
// Простой класс для извлечения верительных данных из файла .ini

class Credentials
{
  var $key_array;

  function Credentials() {
    $this->key_array = parse_ini_file("..\conf\cloud.ini", true);
  }

  function getCredential($group, $key) {
    return $this->key_array[$group][$key];
  }
}
?>

Этот класс с помощью функции PHP parse_ini_file() считывает значения в формате файла .ini. Первым аргументом функции является имя файла, а второй аргумент предписывает PHP обрабатывать файл как отдельные разделы. Это означает, что $key_array является двумерным массивом. Ключи массива — amazon и nirvanix в первом измерении и accesskey, SecretKey, appKey и т.д. во втором. Класс Credentials предоставляет метод getCredential() для извлечения значения из файла .ini. Вместо того, чтобы вставлять верительные данные в код каждого файла PHP, мы начнем свои примеры со строк, подобных следующим:

Листинг 4. Создание и использование объекта Credentials
<?php 
require_once 'Credentials.php'; 
$creds = new Credentials; 
$s3 = new Zend_Service_Amazon_S3($creds->getCredential('amazon', 'accessKey'), 
$creds->getCredential('amazon', 'secretKey'));

Это займет немного больше времени, но зато, когда это сделано, ваши верительные данные будут определены раз и навсегда. Теперь при необходимости их изменить это не придется делать в каждом PHP-файле.

Несколько замечаний:

  • Обратите внимание, что файл .ini находится не в корневом каталоге документов на Web-сервере. Он расположен в каталоге conf, равноправном с корневым каталогом документов. По очевидным причинам нежелательно держать файл верительных данных там, где его может видеть неавторизованный пользователь.
  • С другой стороны, все PHP-файлы, упоминаемые в этой статье, хранятся в корневом каталоге документов Web-сервера.
  • Помните, что комментарии в файле .ini начинаются с точки с запятой; комментарии в стиле PHP не допускаются.

О примере приложения

Одна из первых служб, которую можно отнести к категории облачных вычислений, Amazon S3, представляет собой распределенную файловую систему, дающую неограниченные возможности по хранению данных в Интернете. Модель данных S3 состоит из двух частей: блока (bucket) и объектов (object). Блок может включать бесконечное количество объектов, каждый из которых содержит данные и метаданные. Блок не может содержать другой блок. При создании блока имя блока должно быть уникальным по отношению ко всем пользователям S3. Объект, когда он создан, можно только заменить или удалить; изменить объект нельзя. При создании объекта можно установить параметры управления доступом к нему. По умолчанию объекты являются частными, но их можно открывать для общего доступа.

Наш пример приложения представляет собой менеджер файлов на базе Web для S3. Используя класс Zend_Service_Amazon_S3, мы создадим PHP-страницы, которые позволяют:

Присоединяйтесь к группам облачных вычислений My DeveloperWorks

Делитесь ресурсами с другими разработчиками и обсуждайте такие темы, как многоразовая и настраиваемая архитектура, инструменты и приложения облачных вычислений, в группах My DeveloperWorks Cloud Computing и My DeveloperWorks Cloud Computing Central.

Вы не участник My DeveloperWorks? Так присоединяйтесь!

  • Просматривать все блоки в своей учетной записи S3
  • Создавать новый блок
  • Посматривать все объекты в блоке
  • Создавать новый объект
  • Удалять объект
  • Удалять блок

Приложение пишется в виде двух файлов PHP: s3.php и bucketlist.php. Файл S3.php отображает все блоки вашей учетной записи и позволяет создавать новые блоки или удалять существующие. Файл Bucketlist.php отображает все объекты заданного блока. С его помощью можно создавать новые объекты и удалять существующие. Файл bucketlist.php отображает также метаданные о каждом объекте и обеспечивает прямую связь с объектом на Amazon. (Если доступ к этому объекту не разрешен, вы, естественно, получите сообщение об ошибке.)


Создание объекта Zend_Service_Amazon_S3

Как и следовало ожидать, первым шагом является создание объекта Zend_Service_Amazon_S3. Функция конструктора принимает два параметра: ваш ключ доступа на Amazon и секретный ключ. Используйте вышеупомянутый класс Credentials.

Листинг 5. Создание объекта Zend_Service_Amazon_S3
<?php 
require_once 'Zend/Service/Amazon/S3.php'; 
require_once 'Credentials.php'; 
// Инициализация верительных данных и создание объекта S3 
$creds = new Credentials; $s3 = new Zend_Service_Amazon_S3(
$creds->getCredential('amazon', 'accessKey'), 
$creds->getCredential('amazon', 'secretKey')
); ?>

С этих строк начинаются оба PHP-файла. И для блоков, и для объектов большую часть работы выполняет созданный здесь объект $s3.


Листинг всех блоков учетной записи S3

Когда пользователь загружает s3.php, он видит список всех блоков своей учетной записи Amazon. Экран построен очень просто.

Рисунок 1. Список блоков учетной записи
Список блоков учетной записи

Метод getBuckets() возвращает массив имен блоков. Имя каждого блока отформатировано как ссылка; щелкнув на ссылке, пользователь попадает на страницу bucketlist.php. Рядом с каждым именем блока есть кнопка Delete, которая позволяет удалить блок целиком. Код для удаления блока обсуждается ниже. Вот как генерируются строки таблицы.

Листинг 6. Создание списка блоков
<p>Вот ваши блоки:</p>
<table border='1' cellpadding='5'>
  <?php

  // Создание строки таблицы для каждого блока.
  $list = $s3->getBuckets();
  foreach($list as $bucket) {
    echo "<tr><td>&nbsp;<a href='bucketlist.php?bucketname=$bucket'>$bucket</a>";
    echo "</td><td>";

    $contents = $s3->getObjectsByBucket($bucket);
    if (count($contents)) {
      echo "<form action='$PHP_SELF' method='post' ";
      echo "onsubmit='return confirm(\"Bucket $bucket is not empty! Do you ";
      echo "really want to delete it?\");'>";
      echo "<input type='hidden' name='deleteeverything' value='1'/>";
    }
    else {
      echo "<form action='$PHP_SELF' method='post' ";
      echo "onsubmit='return confirm(\"Do you really want to delete ";
      echo "bucket $bucket?\");'>";
    }
    echo "<input type='hidden' name='buckettodelete' value='$bucket'/>";
    echo "<input type='submit' value='Delete'>";
    echo "</form></td></tr>";
  }
  ?>
  </table>

Обратите внимание на то, что ссылка передает имя блока файлу bucketlist.php.


Создание нового блока

Создание нового блока может оказаться затруднительным в связи с ограничениями, которые Amazon налагает на имена блоков:

  • Они должны быть длиной от трех до 63 символов.
  • Они могут содержать только строчные буквы, цифры, точки и дефисы. Более ранние версии S3 допускали знаки подчеркивания в именах блоков. При обращении к давней учетной записи S3 можно увидеть блоки, которые нарушают это правило.
  • Они должны начинаться с цифры или буквы.
  • Они не могут быть IP-адресом (10.14.14.107 не допускается).
  • Они не могут оканчиваться дефисом.
  • Они не могут содержать дефисы рядом с точками (doug-.tidwell не допускается).

Класс Zend_Service_Amazon_S3 содержит метод с именем _validBucketName(), который выполняет проверку имени блока. К сожалению, код этого метода не согласуется с последними соглашениями об именах, принятыми Amazon. Не исключено, что имя блока пройдет тест _validBucketName(), но при передаче запроса в Amazon будет отвергнуто.

Форма для ввода имени нового блока проста.

Листинг 7. Форма для создания нового блока
<h2>Create a new bucket</h2>
<form action="<?= $PHP_SELF ?>" method="post">
  <p>Enter a name for your new bucket: 
    <br/><br/>
    <input type="text" name="newbucketname" size="63"/>
    <br/><br/>
    <i>A bucket name can contain only lowercase letters, 
    periods and dashes, <br/>it should start with a
    letter or digit, and it can't be an IP address.</i>
    <br/><br/>
    <input type="submit" value="Create bucket"/>
  </p>
  </form>

Обратите внимание, что форма передает имя нового блока сама себе. Нажатие кнопки Создать блок активизирует файл PHP с новым именем блока.

Рисунок 2. Форма для создания нового блока
Форма для создания нового блока

PHP-код создания нового блока проверяет, допустимо ли имя. Если имя проходит проверку, предусмотренную классом Zend_Service_Amazon_S3, код вызывает метод createBucket (). Ненулевой ответ означает, что блок создан успешно; нулевой — что блок уже существует. Любые более серьезные ошибки рассматриваются как исключительные ситуации и обрабатываются с максимально возможной аккуратностью.

Листинг 8. Создание нового блока
<?php

if (array_key_exists('newbucketname', $_POST) &&
    strlen($_POST['newbucketname']) > 0) {
  try {
    if ($s3->_validBucketName($_POST['newbucketname'])) {
      $responseCode = $s3->createBucket($_POST['newbucketname']);
      if ($responseCode)
        echo "The bucket ".$_POST['newbucketname']." was created successfully.";
      else
        echo "The bucket ".$_POST['newbucketname']." already exists.";
    }

    else
      echo "Sorry, but ".$_POST['newbucketname']." isn't a valid bucket name.";
  }

  catch (Zend_Service_Amazon_S3_Exception $s3e) {
    ...
  }

  catch (Zend_Uri_Exception $urie) {
    ...
  }
  }

Список всех объектов в блоке

Нажатие имени блока в s3.php вызывает файл bucketlist.php. Этот файл отображает все объекты блока вместе с метаданными о каждом объекте, а также ссылку на его содержимое.

Рисунок 3. Список объектов блока
Список объектов блока

Zend предоставляет чрезвычайно полезный метод getObjectsByBucket(). Получая имя блока, getObjectsByBucket() возвращает массив имен объектов. Для отображения метаданных код вызывает метод GetInfo() для каждого элемента блока. Вызывать GetInfo() так много раз крайне неэффективно, в рабочем приложении этого делать нельзя.

Листинг 9. Извлечение и отображение метаданных
$stuff = $s3->getObjectsByBucket($bucketName);

if (count($stuff)) { 
?>
  <table border='1' cellpadding='5'>
  ...
  <?php  
  foreach ($stuff as $name) {
  ?>
    <tr>
      <?php
        echo "<td>&nbsp;<a href='$s3_url/$bucketName/$name'>$name</a></td>";
        $metadata = $s3->getInfo($bucketName."/".$name);
      ?>
      <td style='text-align: right;'>&nbsp;
        <?= number_format($metadata['size']) ?></td>
      <td>&nbsp;<?= $metadata['type'] ?></td>
      <td>&nbsp;<?= date("j M Y - H:i", $metadata['mtime']) ?></td>
      <td>
      ?>

Создание нового объекта

Это самая сложная задача в данном примере. Для создания нового объекта имя объекта должно соответствовать правилам Amazon, и данные объекта должны быть доступны. Форма предлагает пользователю выбрать файл, политику доступа и при желании имя объекта.

Рисунок 4. Форма для создания нового объекта
Форма для создания нового объекта

Код принимает файл, выбранный кнопкой Browse, и комбинирует имя объекта (если таковое имеется) с базовым именем файла для создания нового объекта. Например, если выбран файл C:\Documents и Settings\My Documents\My Pictures\doug.jpg и имя объекта pictures, код PHP пытается создать объект с именем pictures/doug.jpg.

Для успешной загрузки файла HTML-форма должна использовать метод POST и устанавливать значение multipart/form-data его атрибута ENCTYPE.

Рисунок 10. Форма для создания нового объекта
<h2>Add an object to this bucket</h2>
<form action='<?= $PHP_SELF ?>' method='POST' enctype='multipart/form-data'>
  <input type='hidden' name='bucketname' value='<?= $bucketName ?>'/>
  <input type='file' name='objecttoadd'/>
  <p>Enter text to be appended to the object name (optional): 
  <input type='text' name='objectname'/> 
  <br/><i>Example: pictures/2009</i>
  <br/><br/>Who can see this object:
  <input type='radio' name='permissions' value='private'>Just me</input> &nbsp;
  <input type='radio' name='permissions' value='public' checked>Anybody</input>
  <br/><br/>
  <input type='submit' value='Add this object'/>
  </p>
  </form>

Код для создания нового объекта выглядит как в листинге 11.

Листинг 11. Создание нового объекта
try {
    $baseFileName = basename($_FILES['objecttoadd']['name']);
    if (strlen($_POST['objectname'])) 
      $newFileName = $_POST['objectname']."/".$baseFileName;
    else
      $newFileName = $baseFileName;
    $escapedFileName = str_replace(array("\\", "_", ":"), "-", $newFileName);

    if ($_POST['permissions'] == 'private')
      $permissions = array(Zend_Service_Amazon_S3::S3_ACL_HEADER 
                           => Zend_Service_Amazon_S3::S3_ACL_PRIVATE);
    else
      $permissions = array(Zend_Service_Amazon_S3::S3_ACL_HEADER 
                           => Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ);

    $s3->putObject($bucketName."/".$escapedFileName, 
                   file_get_contents($_FILES['objecttoadd']['tmp_name']),
                   $permissions);
    echo "The object $escapedFileName was created successfully.";
  }

  catch (Zend_Service_Amazon_S3_Exception $s3e) {
    ...
  }

  catch (Zend_Http_Client_Exception $hce) {
    ...
    }

Этот код с помощью функций PHP basename() и str_replace() получает базовое имя файла и заменяет каждую обратную косую черту, знак подчеркивания или точку дефисом. Для определения политики доступа к новому объекту используется значение permissions из формы. Если пользователь выбирает "Just me" (только я), объект помечается как private; в противном случае он становится общедоступным. (S3 и Zend Framework поддерживают еще два варианта политики доступа: общий доступ к объекту только для определенных аутентифицированных пользователей S3 и разрешение общего доступа к объекту с условием, что Amazon будет выставлять заказчику объекта счет за полосу пропускания, необходимую для его доставки.)


Удаление объекта

Если пользователь нажимает кнопку Delete рядом с объектом, его просят подтвердить свой выбор.

Рисунок 5. Удаление объекта
Удаление объекта

Кнопка Delete и текст в окне сообщений генерируются при создании листинга блока. Код PHP для создания кнопок Delete представлен в листинге 12.

Листинг 12. Форма для подтверждения удаления объекта
<form action='<?= $PHP_SELF ?>' method='post'
  onsubmit='return confirm("Do you really want to delete this object?");'>
  <input type='hidden' name='objecttodelete' value='$name'/>
  <input type='hidden' name='bucketname' value='$bucketName'/>
  <input type='submit' value='Delete'/>
  </form>

Как и все формы для создания или удаления блоков или объектов, эта форма самоактивизируется. Функция JavaScript confirm() отменяет действие, если пользователь нажимает кнопку Cancel. Предположим, что пользователь решил продолжить удаление объекта; код для фактического удаления довольно прост.

Листинг 13. Удаление объекта
try {
  $s3->removeObject($bucketName."/".$_POST['objecttodelete']);
}

catch (Zend_Service_Amazon_S3_Exception $s3e) {
  ...
  }

Страница перезагружается с обновленным отображением нового содержимого блока.

Примечание: Amazon S3 — это распределенная файловая система, способная выдерживать многочисленные отказы отдельных устройств. При добавлении или удалении объекта иногда возникает задержка распространения изменений по системе. В некоторых случаях перезагруженная страница показывает удаленный объект, как будто он еще в блоке, часто с нулевым размером. Перезагрузка страницы обычно предоставляет S3 достаточно времени для самосинхронизации.


Удаление блока

Наиболее радикальная операция, поддерживаемая нашим примером - это удаление блока. Так как имена блоков должны быть уникальными по всей S3, есть вероятность, что пользователь может удалить блок, а позже, попытавшись восстановить его, увидит, что кто-то уже создал блок с таким именем. Еще более мощным (или опасным) наш пример делает то, что он позволяет удалить непустой блок. В S3 не предусмотрена возможность удаления непустого блока. Здесь эта функция реализована с помощью удобного метода, предоставленного Zend.

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

Рисунок 6. Удаление пустого блока
Удаление пустого блока

Если пользователь нажимает кнопку Delete для непустого блока, он получает более тревожное сообщение.

Рисунок 7. Удаление непустого блока
Удаление непустого блока

Является ли данный блок пустым, определяется при создании списка блоков. В листинге 14 приведен код PHP, который создает список блоков.

Листинг 14. Генерация предупреждений для непустых блоков
  $list = $s3->getBuckets();
  foreach($list as $bucket) {
    ...

    $contents = $s3->getObjectsByBucket($bucket);
    if (count($contents)) {
      echo "<form action='$PHP_SELF' method='post' ";
      echo "onsubmit='return confirm(\"Bucket $bucket is not empty! Do you ";
      echo "really want to delete it?\");'>";
      echo "<input type='hidden' name='deleteeverything' value='1'/>";
    }
    else {
      echo "<form action='$PHP_SELF' method='post' ";
      echo "onsubmit='return confirm(\"Do you really want to delete ";
      echo "bucket $bucket?\");'>";
    }
    echo "<input type='hidden' name='buckettodelete' value='$bucket'/>";
    echo "<input type='submit' value='Delete'>";
    echo "</form></td></tr>";
  }
  ?>
  </table>

Когда код PHP создает таблицу с перечислением всех блоков в учетной записи пользователя, он с помощью метода getObjectsByBucket() получает массив всех имен объектов в каждом блоке. Если блок не пустой, код генерирует строгое сообщение с запросом подтверждения.

Фактическую работу по удалению блока код выполняет в несколько этапов. Сначала он проверяет правомерность имени блока. Затем - пустой ли это блок. Если блок пустой, он удаляется. Если нет, код проверяет параметр deleteeverything. Если значение этого параметра True, код все равно удаляет блок. Для этого используется метод cleanBucket(), предоставленный Zend Framework. Метод cleanBucket() получает список всех объектов блока, затем удаляет их по одному, пока блок не окажется пустым. Когда блок в итоге опустеет, он удаляется.

Листинг 15. Удаление блока
try {
  if ($s3->_validBucketName($_POST['buckettodelete'])) {
    $stuff = $s3->getObjectsByBucket($_POST['buckettodelete']);
    if (!count($stuff)) 
      $responseCode = $s3->removeBucket($_POST['buckettodelete']);
    else 
      if (array_key_exists('deleteeverything', $_POST)) {
        $s3->cleanBucket($_POST['buckettodelete']);
        $responseCode = $s3->removeBucket($_POST['buckettodelete']);
      }
      ...

Заключение

В этой статье описан браузер блоков, построенный на базе класса Zend_Service_Amazon_S3. Объекты, хранящиеся в "облаке", можно просматривать, удалять или заменять. Также можно легко загружать новые объекты. Лучше всего то, что нет необходимости в вызовах REST или SOAP. Программисту, использующему Zend Framework, не нужно вычислять сигнатуры или рассматривать коды ответов HTTP, он просто работает со своими данными. В последующих статьях этой серии будут представлены другие классы Zend, которые упрощают облачные вычисления.


Загрузка

ОписаниеИмяРазмер
Пример кодаos-php-zend1-zendcloud_s3.zip7 KБ

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=489252
ArticleTitle=Применение PHP в облачных вычислениях: Часть 1. Работа с Amazon S3 при помощи Zend Framework
publish-date=05122010