Контроллер для URI не вызывается, используя аннотации
У меня есть контроллер Symfony2, который возвращает 500, с этой записи журнала:
[2014-03-26 01:25:48] request.INFO: Matched route "searchtempestsite_direct_sponsored" (parameters: "_controller": "SearchTempest\Bundle\SiteBundle\Controller\SearchController::DirectResultsSponsoredAction", "_route": "searchtempestsite_direct_sponsored") [] []
[2014-03-26 01:25:48] request.CRITICAL: Uncaught PHP Exception InvalidArgumentException: "The controller for URI "/search/direct/sponsored" is not callable." at [...]/releases/20140326082503/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php line 82 {"exception":"[object] (InvalidArgumentException: The controller for URI \"/search/direct/sponsored\" is not callable. at [...]/releases/20140326082503/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php:82)"} []
Действие контроллера определяется как
/**
* Display the Sponsored Results frame in Direct Results
*
* @return Symfony\Component\HttpFoundation\Response
*
* @Route("/search/direct/sponsored", name="searchtempestsite_direct_sponsored")
*/
public function DirectResultsSponsoredAction()
{
$response = $this->get('legacy_bridge')
->request('direct_sponsored.php');
$parameters = $response->getTemplateParameterBag();
$content = $this->renderView(
'SearchTempestSiteBundle:Search:directSponsored.html.twig',
$parameters->all()
);
$response->setContent($content);
return $response;
}
Что странно, все работает правильно, как в режиме dev, так и в режиме prod, на моем локальном тестовом сервере. Только когда я пытаюсь развернуть это на рабочем сервере, используя capifony, я получаю эту ошибку. Однако все другие маршруты, определенные под тем же контроллером, работают должным образом. Только этот новый провал. Вот рабочий маршрут:
/**
* Display the Direct Results index
*
* @return Symfony\Component\HttpFoundation\Response
*
* @Route("/search/direct", name="searchtempestsite_direct")
*/
public function DirectResultsAction()
{
$response = $this->get('legacy_bridge')
->request('adv_control.php');
$parameters = $response->getTemplateParameterBag();
$content = $this->renderView(
'SearchTempestSiteBundle:Search:directResults.html.twig',
$parameters->all()
);
$response->setContent($content);
return $response;
}
Я попытался изменить маршрут (с /search/direct/ спонсорский на /search/direct_sponsored), но я все равно получил ту же ошибку 500 на новом пути. (С новым путем в ошибке, конечно.)
Кроме того, я как бы застрял в том, что нужно попробовать, если не копаться в кишечнике кода Symfony и Sensio, чтобы проследить, как он согласовывает эти аннотации. Предположительно, это как-то связано с установкой на сервер, поскольку он работает локально, но, насколько я могу судить, все должно быть одинаково. Оба используют composer для установки зависимостей, поэтому весь код поставщика должен быть одинаковым. Мы не разделяем кеш между выпусками на сервер, поэтому он работает с новым кешем. (Capifony вызывает кэш приложения / консоли:warmup --env=prod.)
Похоже, что обычно эта ошибка происходит из-за того, что спецификация контроллера в yml маршрутизации не соответствует названию функции Action или, наоборот, функция не является общедоступной. Ни то, ни другое здесь не применимо.
Я рад предоставить любую дополнительную запрашиваемую информацию.
Изменить: сейчас работает, хотя я ничего не изменил. Я думаю, что происходит с многоступенчатым расширением Capifony. Мы работаем в три этапа (разработка, бета, прод). Я проверял это на этапе разработки, но по какой-то причине попытался развернуть его на других этапах, чтобы увидеть, будет ли это иметь какое-то значение. Сначала этого не произошло, но после того, как я развернулся на всех трех, когда я затем развернул на любом из них, маршрут начал работать.
Редактировать 2: Многоступенчатая была большая часть проблемы, но это было еще не все. Смотрите мой ответ ниже.
1 ответ
Оказывается, проблема была из-за ApcClassLoader. Эти две строки в app.php инструктируют sf2 использовать APC для кэширования расположений (путей файловой системы) различных классов, используемых приложением:
$loader = new ApcClassLoader('sf2', $loader);
$loader->register(true);
Вы должны изменить sf2 на уникальный префикс, чтобы избежать конфликтов ключей кеша. Мы сделали это, но использовали один и тот же "уникальный" префикс для каждого из наших этапов, что означало, что когда мы запускаем развертывание и очищаем кэш-память apc, то на любом из этапов, к которому был получен доступ первым, будут заданы местоположения файлов в кэше и те же файлы. будет использоваться для всех этапов. Поскольку это загруженный сайт, это означало, что файлы рабочих этапов использовались для других этапов.
Решение должно состоять в том, чтобы просто развернуть отдельную версию app.php для каждого этапа с отдельным префиксом. (Или передайте переменную для префикса; как бы вы этого не хотели.) Однако я также обнаружил, что очень редко предыдущая версия того же этапа загрязняла кэш, даже если мы очищаем кэш APC ([как описано здесь). ][1]) сразу после развертывания. В настоящее время я не могу объяснить, почему это происходит, поскольку, похоже, это происходит, даже если мы добавляем задержку перед очисткой, если какие-либо запросы выполняются.
Поэтому сейчас мы просто собираемся использовать composer вместо apc для сохранения карты классов, как описано в разделе "Использование функциональности карты классов Composer" здесь: http://symfony.com/doc/current/book/performance.html
Изменить: Ах, и это объясняет последний момент, как более ранние выпуски загрязняли кеш даже после выпуска новых: /questions/23915256/simvolyi-capistrano-kotoryie-keshiruyutsya/23915265#23915265. Мы используем символические ссылки для развертывания, а PHP кэшировал старые маршруты символических ссылок, в результате чего предыдущее развертывание заполняло кэш до обновления символической ссылки. Ответ, связанный там, объясняет, как обойти это.