Уже совсем скоро (хочется надеяться) будет выпущен первый релиз 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! ))
Кстати, в рассылке пишут, что 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 года.
Действительно, очень весомый аргумент, спасибо)