Silex micro-framework and Twig: enable debug - php

My question: How do I permit use of debug in Twig templates within Silex?
I'm playing around with the Silex micro-framework (a PHP framework that leverages Symfony).
When using the Twig template system with it I wanted to output a particular object. Normally I would do this with var_dump($app); and in Twig with {% debug app %}.
My problem is getting debug (and setting Silex's own debug to true does not help with Twig) to work with Silex. Out of the box a call to debug will result in an error message:
Twig_Error_Syntax: Unknown tag name "debug" in...
The call to debug looks like this:
{% debug app %}
I have found references to how to configure Twig's config.yml file to correctly use debug but Silex does not use a config file for Twig.
Silex does say you can set options via passing an associative array to twig.options and while Twig docs say you can pass an environment option like:
$twig = new Twig_Environment($loader, array('debug' => true));
Trying to pass it in Silex like:
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.options' => array('debug' => true),
));
Does not work. Is this the wrong sort of option? Simply incorrect formatting? I've no idea and nothing I have tried works.
I sense myself getting into "wheel spinning" mode so I am asking here on SO in hopes that I can move on to more productive work this morning. :)
(ugh... how's that for a hyper-specific StackOverflow question?)
Solution: (all this just to get var_dump like functionality... oh my): This was a bit of a pain in the butt, to be honest, and the Silex docs were of no help whatsoever in discovering this but here is what I had to do to get this to work.
First load the Twig autoloader:
$app['autoloader']->registerPrefixes(array(
'Twig_Extensions_' => array(__DIR__.'/vendor/Twig-extensions/lib')));
Why do you have to register it this way? No idea. How does it actually find the autoloader? No idea. But it works.
Register the provider and set the debug option:
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
'twig.class_path' => __DIR__.'/vendor/Twig/lib',
'twig.options' => array('debug' => true), //<-- this is the key line to getting debug added to the options
));
And finally (the best part):
$oldTwigConfiguration = isset($app['twig.configure']) ? $app['twig.configure']: function(){};
$app['twig.configure'] = $app->protect(function($twig) use ($oldTwigConfiguration) {
$oldTwigConfiguration($twig);
$twig->addExtension(new Twig_Extensions_Extension_Debug());
});
To be honest, I think that's enough Silex for me.
Credit for this solution goes to Nerdpress.
*ninja edit: a year and a half later I have to say that Silex was a dud for me. I have been using Slim for all micro-framework needs and it is fantastic. Gets the job done quickly, cleanly, simply and easily.

I completely removed the old answer to show the output from a little example-app I built:
composer.json:
{
"require": {
"silex/silex": "1.*",
"twig/twig": "1.*",
"twig/extensions": "*"
}
}
app.php:
require_once __DIR__ . '/../vendor/.composer/autoload.php';
$app = new Silex\Application();
$app['debug'] = true;
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__ . '/views',
'twig.options' => array('debug' => true)
));
$app['twig']->addExtension(new Twig_Extensions_Extension_Debug());
$app->get('/', function () use ($app) {
return $app['twig']->render('debug_test.twig', array('app' => $app));
});
$app->run();

for silex ^2.2 using pimple 3 the share() has been removed so use:
$app->extend('twig', function($twig, $app) {
$twig->addExtension(new Twig_Extension_Debug());
return $twig;
});

It has been a while now so I did a small update to the accepted answer, you can use the new extend method of Pimple:
composer.json:
"silex/silex": "~1.3",
"twig/twig": "^1.23",
"symfony/twig-bridge": "^2.7",
"twig/extensions": "^1.3"
index.php (front controller)
/*
* config
*/
//...
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/../templets',
'twig.options' => array('debug' => true),
)
);
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
$twig->addExtension(new Twig_Extension_Debug());
return $twig;
}));
//...
/*
* some static page
*/
$app->get('/', function () use($app) {
return $app['twig']->render('index.twig');
});
$app->run();

Related

Slim application not updating immediately after saving

I am trying out Slim 4 (I've previously used Slim 3 but many things seem to have changed in the new versions of Slim and Twig) and I have a very simple starter application:
<?php
use DI\Container;
use Slim\Views\Twig;
use Slim\Factory\AppFactory;
use Slim\Views\TwigMiddleware;
use Slim\Middleware\ErrorMiddleware;
// Autoload
require 'vendor/autoload.php';
// Create Container using PHP-DI
$container = new Container();
AppFactory::setContainer($container);
// Create the app
$app = AppFactory::create();
// Show meaningful errors with ErrorMiddleware
$errorMiddleware = new ErrorMiddleware(
$app->getCallableResolver(),
$app->getResponseFactory(),
true,
false,
false
);
$app->add($errorMiddleware);
// Set view in Container
$container->set('view', function() {
return Twig::create('views', [
'auto_reload' => true,
'cache' => false
]);
});
$app->add(TwigMiddleware::createFromContainer($app));
// Define routes
$app->get('/', function($req, $res) {
return $this->get('view')->render($res, 'home.twig');
});
$app->get('/heroes', function($req, $res) {
$users = [
'Batman',
'Supes',
'Flash'
];
return $this->get('view')->render($res, 'heroes.twig', [
'users' => $users
]);
});
// Run the app
$app->run();
After several tests I have noticed that if I make a change, for example if I type a different twig file or if I write a different route, changes are not reflected when I refresh the browser. No matter how many times I hit refresh, changes are not reflected until approx. a minute later.
I'm using MAMP PRO v6.6. The project is built with the following dependencies:
{
"require": {
"slim/slim": "^4.10",
"slim/psr7": "^1.5",
"php-di/php-di": "^6.4",
"slim/twig-view": "^3.3"
}
}
I have added auto-reload and cache settings to the Twig creation but this doesn't seem to have any effect.
I ended up going into the php.ini file and commenting out the OPCache block towards the end of the file. That took care of it.

slim/twig-view - TwigExtension template functions not indetified and working

I'm following the steps described here to use twig-view within Slim https://github.com/slimphp/Twig-View/tree/3.1.0#usage but I'm getting the following error on my screen when I try to use anyof the template functions used in TwigExtension
Fatal error: Uncaught Twig\Error\SyntaxError: Unknown "url_for" function.
I have run $ composer require slim/twig-view:^3.0 successfully, my composer.json file looks like this
"require": {
"slim/slim": "4.*",
"slim/psr7": "^1.2",
"league/container": "^3.3",
"slim/twig-view": "^3.0"
},
and this is my code
require_once __DIR__ . '/../vendor/autoload.php';
$container = new \Slim\Factory\Container();
\Slim\Factory\AppFactory::setContainer($container);
$container->add('view', function () {
return \Slim\Views\Twig::create(__DIR__ . '/views', [
'cache' => false,
]);
});
$app = \Slim\Factory\AppFactory::create();
$app->add(\Slim\Views\TwigMiddleware::createFromContainer($app));
require_once __DIR__ . '/../routes.php';
// routes.php
$app->get('/', function ($request, $response, $args) use ($container) {
return $container->get('view')->render($response, 'home.twig', ['foo' => 'test']);
})->setName('home');
// home.twig
...
<body>
Home {{ foo }}
<br>
About
</body>
...
If I remove the url_for from the twig template the page loads fine on the browser. I tried to search for TwigExtension in my codebase and the vendor folder, but can't find any file like that.
Am I doing something wrong here?
Looks like it's because of League container. It seems to be creating a new instance of Twig every time the function gets called $container->get('view') returns a new instance every time instead of referencing the same one. So a workaround would be
$twig = \Slim\Views\Twig::create(__DIR__ . '/views', [
'cache' => false,
]);
$container->add('view', function () use (&$twig) {
return $twig;
});
// Or this instead
$container->add(
'view',
\Slim\Views\Twig::create(__DIR__ . '/views', [
'cache' => false,
])
);

Instantiating twig in a namespaced class

I'm running into some issues trying to figure out how to use Twig in a specific way. I'm trying to write a view class that I can use in my application regardless of what template system is installed, that way the only things I would have to change would be the templates themselves and the view class.
However, when I try and create the twig objects in the class, I get Fatal error: Class 'Template\Twig_Loader_Filesystem' not found errors, and I'm not really sure what I'm missing.
Can someone point me in the right direction?
Here's what I've got so far...
composer.json
{
"name": "Movies",
"description": "Find movies",
"require": {
"ext-curl": "*",
"twig/twig": "~1.0"
},
"autoload": {
"classmap": [
"app/classes"
]
}
}
index.php
require_once 'app/app.php';
use Template\Template;
echo Template::render('hello.html', array('name' => 'bob', 'age' => '33'));
app/app.php
define('APP_DIRECTORY', __DIR__ . '/..');
require_once APP_DIRECTORY . '/vendor/autoload.php';
app/classes/Template.class.php
namespace Template;
class Template {
static function render($template_name, $template_data = array()) {
$loader = new Twig_Loader_Filesystem(APP_DIRECTORY . '/app/templates');
$twig = new Twig_Environment($loader);
return $twig->render('hello.html', array('name' => 'lisa', 'age' => '33'));
}
}
Ok, it turned out whenever I would try and include the twig namespaces on my Template class, I was doing it wrong. Looking at other projects my organization has put together, it turns out just adding
use Twig_Environment;
use Twig_Loader_Filesystem;
to the class is enough. I wasn't sure if I actually had to add anything, since the Autoloader was included, or if I had to include part of the path, like Twig_Twig/Loader/Filesystem or something like that. Obviously neither of those was working.

Phalconphp OAuth2.0 Wrapper problems

I am trying to get this phalconphp OAuth2.0 wrapper working on my OAuth2.0 server.
The README of this repository is not very clear on how to use the namespaces.
I have followed the guide but I keep getting the following error :
Fatal error: Class 'Sum\Oauth2\Server\Storage\Pdo\Mysql\Client'
not found in C:\localhost\oauth2-phalcon\public\index.php on line 56
Here is my index.php file :
<?php
require __DIR__."/../vendor/autoload.php";
// Setup IIS Rewrite Rules
// Enable the verbs GET,PUT,POST,DELETE
// Check URL Scan for dissallowed seperators eg ; :
$config = new \Phalcon\Config([
'database' => [
'oauth' => [
'host' => 'localhost\test',
'port' => 1433,
'instance' => 'INSTANCENAME',
'username' => 'test',
'password' => 'test',
'dbname' => 'oauth',
'pdoType' => 'sqlsrv',
'dialectClass' => '\Twm\Db\Dialect\Mssql'
],
],
# ...
]);
# Register The Lib to the loader
$loader = new \Phalcon\Loader();
$loader->registerNamespaces([
"Twm\Db\Adapter\Pdo" => "../app/library/db/adapter/",
"Twm\Db\Dialect" => "../app/library/db/dialect/",
"League" => "../vendor/league/oauth2-server/src/League/OAuth2/Server",
//"Sum\Oauth2\Server\Storage\Pdo" => "../Oauth2/Server/Storage/Pdo/Mysql",
"Sum" => "../Oauth2/Server/Storage/Pdo/Mysql",
//"Sum\Oauth2\Server\Storage\Pdo\Mysql" => "../Oauth2/Server/Storage/Pdo/Mysql "
# ...
])->register();
$app = new \Phalcon\Mvc\Micro();
# set as service
$app->setService('oauth', function() use ($config) {
// HERE! We use our custom MSSQL Adapter
//$oauthdb = new Phalcon\Db\Adapter\Pdo\Mysql($config->database->oauth->toArray());
$oauthdb = new \Twm\Db\Adapter\Pdo\Mssql($config->database->oauth->toArray());
$server = new \League\OAuth2\Server\Authorization(
new \Sum\Oauth2\Server\Storage\Pdo\Mysql\Client($oauthdb),
new Sum\Oauth2\Server\Storage\Pdo\Mysql\Session($oauthdb),
new Sum\Oauth2\Server\Storage\Pdo\Mysql\Scope($oauthdb)
// new \Sum\Oauth2\Server\Client($oauthdb),
//new \Sum\Oauth2\Server\Session($oauthdb),
//new \Sum\Oauth2\Server\Scope($oauthdb)
);
# Not required as it called directly from original code
# $request = new \League\OAuth2\Server\Util\Request();
# add these 2 lines code if you want to use my own Request otherwise comment it
$request = new \Sum\Oauth2\Server\Storage\Pdo\Mysql\Request();
$server->setRequest($request);
$server->setAccessTokenTTL(86400);
$server->addGrantType(new League\OAuth2\Server\Grant\ClientCredentials());
});
$app->get('/hello', function() use($world){
$world = "world";
echo "hello {$world}:)";
});
$app->get('/access', function () use ($app) {
try {
$params = $app->oauth->getParam(array('client_id', 'client_secret'));
echo json_encode(
$app->oauth
->getGrantType('client_credentials')
->completeFlow($params)
);
} catch (\League\OAuth2\Server\Exception\ClientException $e) {
echo $e->getTraceAsString();
} catch (\Exception $e) {
echo $e->getTraceAsString();
}
});
$app->handle();
//echo $app->handle()->getContent();
The project repository for the phalcon wrapper is here :
https://github.com/sumeko/phalcon-oauth2
I have contacted the author already but he is not replying to my emails.
I appreciate any help or advice, thanks.
UPDATE
So I solved my issue. Basically you need to have the standard OAuth2 library installed via composer and the phalconphp OAuth2 wrapper.
That solved it :)
This might be a long shot, but the problem might be with the autoloader that you are explicitly defining. If you use composer's autoload, you don't need to include Sum namespace in Phalcon's loader. Remove all vendor-specific paths from $loader->registerNamespaces() and only use require __DIR__ . "/../vendor/autoload.php" for that.
Also, it's often more convenient use composer's autoloader for your internal things too, e.g.:
{
"require": {
"phpunit/dbunit": "*",
"phpunit/phpunit": "*",
"…": "…"
},
"autoload": {
"psr-0": {
"": "../src"
}
}
}
So I solved my issue. Basically you need to have the standard OAuth2 library installed via composer and the phalconphp OAuth2 wrapper. That solved it :)

How to use the PHP template engine in Twig instead of the Twig syntax within Silex

In Silex I am able to use Twig templates but I want to use the PHP engine of Twig, instead of the Twig syntax. For example this guide describes how to do it for Symfony but not Silex.
My Silex index.php looks like:
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
$app->get('/', function() use ($app) {
return $app['twig']->render('index.html.php', array(
'name' => 'Bob',
));
});
My index.html.php looks like:
<p>Welcome to the index <?php echo $view->name; ?></p>
When I run the app in the browser and view the source, I see the literal string <?php echo $view->name; ?> which hasn't been executed.
I suspect there may be a Twig config setting to tell it I want to use the PHP style templates. To clarify, if I use the Twig syntax instead, e.g.:
<p>Welcome to the index {{ name }} </p>
Then it works and I see the name Bob, therefore I know this is not a web server or PHP config problem.
If you want to mimic this behaviour in Silex, you would need to install the TwigBridge via Composer. Then build the templating service the same way Symfony does.
This solution works as I have tested it successfully.
<?php
require __DIR__.'/vendor/autoload.php';
use Silex\Application;
use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\DelegatingEngine;
use Symfony\Bridge\Twig\TwigEngine;
$app = new Application();
$app['debug'] = true;
// Register Twig
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
// Build the templating service
$app['templating.engines'] = $app->share(function() {
return array(
'twig',
'php'
);
});
$app['templating.loader'] = $app->share(function() {
return new FilesystemLoader(__DIR__.'/views/%name%');
});
$app['templating.template_name_parser'] = $app->share(function() {
return new TemplateNameParser();
});
$app['templating.engine.php'] = $app->share(function() use ($app) {
return new PhpEngine($app['templating.template_name_parser'], $app['templating.loader']);
});
$app['templating.engine.twig'] = $app->share(function() use ($app) {
return new TwigEngine($app['twig'], $app['templating.template_name_parser']);
});
$app['templating'] = $app->share(function() use ($app) {
$engines = array();
foreach ($app['templating.engines'] as $i => $engine) {
if (is_string($engine)) {
$engines[$i] = $app[sprintf('templating.engine.%s', $engine)];
}
}
return new DelegatingEngine($engines);
});
// Render controllers
$app->get('/', function () use ($app) {
return $app['templating']->render('hello.html.twig', array('name' => 'Fabien'));
});
$app->get('/hello/{name}', function ($name) use ($app) {
return $app['templating']->render('hello.html.php', array('name' => $name));
});
$app->run();
You would need at least these dependencies to achieve this in your composer.json
"require": {
"silex/silex": "~1.0",
"symfony/twig-bridge": "~2.0",
"symfony/templating": "~2.0",
"twig/twig": "~1.0"
},

Categories