Уже совсем скоро (хочется надеяться) будет выпущен первый релиз Symfony 2. А по сему пробуем применить его на практике.
Этим постом я планирую начать небольшую серию публикаций про работу с Symfony 2 и Facebook на php 5.3. Что из этого выйдет – время покажет, а пока делаем первые шаги:
Для начала качаем sandbox Symfony 2: http://symfony-reloaded.org/downloads/sandbox_2_0_PR2.zip
Я экспериментирую на локальной машине с denwer‘ом. У вас може быть XAMPP, MAMP или любая другая работоспособная комбинация из вебсервера и PHP 5.3.2+ (у меня установлен PHP 5.3.3).
Создаем директорию z:hometest-project.com и распаковываем содержимое сандбокса в нее. Получится примерно следующее:
z:hometest-project.com
hello
src
web
Web не мудрствуя лукаво переименуем в www чтобы не возиться с ручным конфигом и hosts (в этом случае денвер сделает все настройки самостоятельно. Иначе – вам придется сделать их самим, что займет лишние 2 минуты). Не забываем рестартануть денвера.
Далее качаем с github тем или иным способом facebook php sdk: http://github.com/facebook/php-sdk/. Нас оттуда интересует файл src/facebook.php. Сразу предупрежу – я поступил немного грязно и разделил этот класс на два (их там и есть два, собственно Facebook и FacebookApiExcetion), а также разместил в namespace Facebook. Выглядит это примерно так:
// src/vendor/facebook/Facebook/Facebook.php
<?php
namespace Facebook;
if (!function_exists('curl_init')) {
throw new Exception('Facebook needs the CURL PHP extension.');
}
if (!function_exists('json_decode')) {
throw new Exception('Facebook needs the JSON PHP extension.');
}
/**
* Provides access to the Facebook Platform.
*
* @author Naitik Shah <naitik@facebook.com>
*/
class Facebook
{
И вот так:
// src/vendor/facebook/Facebook/FacebookApiException.php
<?php
namespace Facebook;
/**
* FacebookApiException
*
* @author dmitry.bykadorov@gmail.com
* @version SVN: $Id: $
*/
/**
* Thrown when an API call returns an exception.
*
* @author Naitik Shah <naitik@facebook.com>
*/
class FacebookApiException extends Exception
{
Обратите внимание на строку class FacebookApiException extends Exception. Мы используем стандартный класс Exception, который не принадлежит никакому пространству имен. Ну или принадлежит корневому (стандартному) пространству имен. Честно говоря я пока не очень продвинут в работе с неймспейсами, поэтому в терминологии могу ошибаться ).
Как вы можете видеть, файлы классов расположены в директории src/vendor/facebook/Facebook/. Разработчики Symfony 2 рекомендуют не включать сторонние библиотеки в бандлы или приложения – думаю стоит к этому прислушаться. Поэтому после размещения библиотеки в нашем приложении нам нужно зарегистрировать его в автолоадере. Для того открываем файл src/autoload.php и добавляем туда наш неймспейс в функцию registerNamespaces:
// src/autoload.php //... $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( 'Symfony' => __DIR__.'/vendor/symfony/src', 'Application' => __DIR__, 'Bundle' => __DIR__, 'Doctrine\Common' => __DIR__.'/vendor/doctrine/lib/vendor/doctrine-common/lib', 'Doctrine\DBAL\Migrations' => __DIR__.'/vendor/doctrine-migrations/lib', 'Doctrine\ODM\MongoDB' => __DIR__.'/vendor/doctrine-mongodb/lib', 'Doctrine\DBAL' => __DIR__.'/vendor/doctrine/lib/vendor/doctrine-dbal/lib', 'Doctrine' => __DIR__.'/vendor/doctrine/lib', 'Zend' => __DIR__.'/vendor/zend/library', 'Facebook' => __DIR__.'/vendor/facebook/lib', )); //...
Для реализации тестового примера нам потребуется одна страница, поэтому структуру сандбокса с application bundle hello мы менять не будем (кроме того я пока не придумал зачем нам нужен будет бандл Facebook, поэтому это будет тема следующей статьи).
Создаем iframe приложение Facebook в разделе разработчиков (на этом отдельно не останавливаюсь, статью напишу если попросите). Оттуда нам нужны параметры приложения – app_id, api_key, app_secret. Второй параметр для oAuth не используется, но может будет полезен впоследствии. Наши настройки мы внесем в файл настроек hello/config/config.yml:
# hello/config/config.yml
parameters:
kernel.include_core_classes: false
# facebook
facebook.credentials.app_id: <ваш_app_id>
facebook.credentials.api_key: <ваш_api_key>
facebook.credentials.app_secret: <ваш_app_secret>
facebook.url.canvas: # будет нужен потом
facebook.url.site: # будет нужен потом
Модифицируем HelloController:
// src/Application/HelloBundle/Controller/HelloController.php
<?php
namespace ApplicationHelloBundleController;
use Facebook;
use SymfonyFrameworkFoundationBundleController;
class HelloController extends Controller
{
public function indexAction($name)
{
$app_id = $this->container->getParameter('facebook.credentials.app_id');
$api_key = $this->container->getParameter('facebook.credentials.api_key');
$app_secret = $this->container->getParameter('facebook.credentials.app_secret');
// у меня локально без отключения проверки сертификатов не заработало
FacebookFacebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = 0;
FacebookFacebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = 0;
$facebook = new FacebookFacebook(
array(
'appId' => $app_id,
'secret' => $app_secret,
'cookie' => true,
));
$session = $facebook->getSession();
$me = null;
$uid = null;
// Обращение к API на основе сессии
if ($session)
{
try
{
$uid = $facebook->getUser();
$me = $facebook->api('/me');
}
catch ( FacebookFacebookApiException $e )
{
// пока ничего не делаем
}
}
// login или logout url будут нужны в зависимости от статуса авторизации пользователя
$logoutUrl = '';
$loginUrl = '';
if ($me)
{
$logoutUrl = $facebook->getLogoutUrl();
}
else
{
$loginUrl = $facebook->getLoginUrl();
}
// Обращение к API без сессии
$user = $facebook->api('/dmitry.bykadorov');
return $this->render(
'HelloBundle:Hello:index',
array(
'facebook' => $facebook,
'session' => $session,
'me' => $me,
'logoutUrl' => $logoutUrl,
'loginUrl' => $loginUrl,
'user' => $user,
'uid' => $uid,
)
);
}
}
Теперь нужно модифицировать шаблон: добавить в него декоратор (layout – он теперь не цепляется по умолчанию) и сформируем шаблон:
<?php
<!-- src/Application/HelloBundle/Resources/views/Hello/index.php -->
/**
*
*/
?>
<?php $view->extend('HelloBundle::layout') ?>
<div id="fb-root"></div>
<style>
body {
font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
}
h1 a {
text-decoration: none;
color: #3b5998;
}
h1 a:hover {
text-decoration: underline;
}
</style>
<script>
window.fbAsyncInit = function()
{
FB.init({
appId : '<?php echo $facebook->getAppId(); ?>',
session : <?php echo json_encode($session); ?>, // не фетчим сессию если она уже есть в PHP
status : true, // разрешаем проверку логина
cookie : true, // разрешаем куки для проверки сессии
xfbml : true // разрешаем парсинг XFBML
});
// если юзер вошел надо обновить страницу
FB.Event.subscribe('auth.login', function()
{
window.location.reload();
});
};
(function() {
var e = document.createElement('script');
e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
}());
</script>
<h1><a href="example.php">php-sdk</a></h1>
<?php if ($me): ?>
<a href="<?php echo $logoutUrl; ?>">
<img src="http://static.ak.fbcdn.net/rsrc.php/z2Y31/hash/cxrz4k7j.gif">
</a>
<?php else: ?>
<div>
С использованием JavaScript & XFBML:
<fb:login-button></fb:login-button>
</div>
<div>
Без JavaScript & XFBML:
<a href="<?php echo $loginUrl; ?>">
<img src="http://static.ak.fbcdn.net/rsrc.php/zB6N8/hash/4li2k73z.gif">
</a>
</div>
<?php endif ?>
<h3>Сессия</h3>
<?php if ($me): ?>
<pre><?php print_r($session); ?></pre>
<h3>Вы</h3>
<img src="https://graph.facebook.com/<?php echo $uid; ?>/picture">
<?php echo $me['name']; ?>
<h3>Ваш юзерский объект</h3>
<pre><?php print_r($me); ?></pre>
<?php else: ?>
<strong><em>Вы не подключены.</em></strong>
<?php endif ?>
<h3>Юзер</h3>
<img src="https://graph.facebook.com/dmitry.bykadorov/picture">
<?php echo $user['name']; ?>
Ну и немного подправим декорирующий шаблон:
<!-- src/Application/HelloBundle/Resources/views/layout.php -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<table style="width:700px;margin-left:auto;margin-right:auto;" border="1">
<tr>
<td>
<?php $view->slots->output('_content') ?>
</td>
</tr>
</table>
</body>
</html>
В результате мы поимеем примерно следующее:
So… рубикон перейден, надеюсь мы с вами тут же на бережку не разляжемся а двинемся дальше ))
Have fun! ))

16 Comments
Кстати, в рассылке пишут, что PR3 уже доступна.
Честно говоря за рассылками следить не успеваю ) Вышла – значит посмотрим )
Спасибо, отлично написано!
Видимо уже не буду изучать версию 1.х а сразу начну работу с 2-ой версии, с задатком на будущее, что многие хостеры начнут переход на 5.3 PHP
Symfony 1.x тоже мощный инструмент, не надо его списывать со счетов – года два он нас будет радовать
А Symfony 2 напротив, содержит много инновационных в мире PHP фреймворков идей, поэтому прямо сегодня я бы посоветовал все-таки выполнить Jobeet а после него уже взяться за Symfony 2. Тем не менее это всего лишь моя личная рекоммендация
Правильно, уже многие хостеры перешли на 5.3
Глядя на все это, думаю, а так ли нужны эти namespaces в PHP )))))
Хз. Еще не понял. В Java меня import *** всегда раздражали, да. Типа require once в PHP. Вроде автолоадинг в PHP5 прекратил это. Теперь вот роллбэк к тому же самому но в новом качестве.
Для Денвера можно не переименовывать web > www, Денвер сгенерит хосты и для него. Просто доступ к нему будет через поддомен: http://web.yourhost/
Согласен ) Просто меня поддомен раздражает немного. Чисто субъективно.
Я предпочитаю делать следующую структуру проекта на denwer
project_name – название проекта
project – здесь лежат все исходники
hello
src
web
www – симлинк на project/web – делаю в total плагином NTFS Link
Так избавляюсь от поддоменов и необходимости редактировать файл host
Что за плагин такой? В NTFS есть аналог символических ссылок?
Это не символические, это хард-ссылки. В ntfs они есть.
По-моему в NTFS для папок все-таки символические ссылки (junction). А для файлов — жесткие.
В win 7 они очень активно юзаются. Например:
- це:Documents and Settings это симлинка на це:Users
- це:Users/username/Application Data это симлинка на це:Users/username/AppData/Roaming
- …
Для создания таких ссылок своими силами есть несколько способов:
- плагин в тотал коммандере
- стороние утилиты (linkd.exe, ln.exe, junction.exe, …) – актуально для винд до висты
- родные утилиты (вроде начиная с висты и вин7): mklink.exe
А почему вы больше склоняетесь к интеграции с facebook при помощи iframe, а не FBML? В пользу iframe можно сказать что есть возможность использования JS-библиотек (мой любимый Jquery=)), но и существует неудобство связанное с IE6/7, теряются переменные сессии (cookies). Было бы очень интересно услышать вашу точку зрения по этому вопросу))
Судя по опыту (>года) FBML тоже теряет сессии.
Теперь к сути вопроса. Почему я против FBML? Я не против! Просто его не поддерживает команда Facebook. Взгляните-ка на roadmap: http://developers.facebook.com/roadmap
Т.о. FBML вымрет (по моим прогнозам) к лету 2011 года.
Действительно, очень весомый аргумент, спасибо)