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.
Related
i am working on facebook messenger bot. I am using Botman (botman.io) without Laravel or botman studio. Version of PHP is 7.4.
Simple hears and reply method works fine, but conversation replying method does not working.
If I try type hi|hello or some greetings, chatbot answer me "Hello! What is your firstname?", then I write my name and chatbot does not returns any text :-/
Can you help me where is a bug?
There is a conversation class:
namespace LiborMatejka\Conversations;
use BotMan\BotMan\Messages\Conversations\Conversation;
use BotMan\BotMan\Messages\Incoming\Answer;
class OnboardingConversation extends Conversation {
protected $firstname;
protected $email;
function askFirstname() {
$this->ask('Hello! What is your firstname?', function (Answer $answer) {
// Save result
$this->firstname = $answer->getText();
$this->say('Nice to meet you ' . $this->firstname);
$this->askEmail();
});
}
public function askEmail() {
$this->ask('One more thing - what is your email?', function (Answer $answer) {
// Save result
$this->email = $answer->getText();
$this->say('Great - that is all we need, ' . $this->firstname);
});
//$this->bot->typesAndWaits(2);
}
public function run() {
// This will be called immediately
$this->askFirstname();
}
}
and there is config:
require_once "vendor/autoload.php";
require_once "class/onboardingConversation.php";
use BotMan\BotMan\BotMan;
use BotMan\BotMan\BotManFactory;
use BotMan\BotMan\Drivers\DriverManager;
use BotMan\Drivers\Facebook\FacebookDriver;
use LiborMatejka\Conversations\OnboardingConversation;
$config = [
// Your driver-specific configuration
'facebook' => [
'token' => 'my_token',
'app_secret' => 'my_secret_app_code',
'verification' => 'verification_code',
],
'botman' => [
'conversation_cache_time' => 0,
],
];
// Load the driver(s) you want to use
DriverManager::loadDriver(\BotMan\Drivers\Facebook\FacebookDriver::class);
// Create an instance
$botman = BotManFactory::create($config);
$botman->hears('ahoj|hi|hello|cau|cus|zdar|zdarec|cago|hey|ciao', function (BotMan $bot) {
$bot->startConversation(new OnboardingConversation);
});
// Start listening
$botman->listen();
Add symfony/cache to your project using composer
composer require symfony/cache
Put following at top of index.php (or other) file where you're setting up BotMan
use BotMan\BotMan\Cache\SymfonyCache;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
Then create your BotMan using the following:
$adapter = new FilesystemAdapter();
$botman = BotManFactory::create($config, new SymfonyCache($adapter));
Then use your $botman variable accordingly, like example below:
$botman->hears('Hi', function (BotMan $bot) {
$bot->typesAndWaits(2);
$bot->reply('Hello and welcome');
$bot->typesAndWaits(2);
$bot->ask('Anything I can do for you today?',function($answer, $bot){
$bot->say("Oh, really! You said '{$answer->getText()}'... is that right?");
});
});
I would rather to use auto-wiring to inject the SymfonyCache anywhere you create the Botman instance, without creating the adapter and cache again and again.
Step 1: config the cache in cache.yaml
framework:
cache:
# botman: cache adapter
app: cache.adapter.filesystem
Step 2: autowiring in services.yaml
services:
BotMan\BotMan\Cache\SymfonyCache:
arguments:
$adapter: '#cache.app'
Step 3: Inject the SymfonyCache where you need, for example in ChatController::message()
public function message(SymfonyCache $symfonyCache): Response
{
....
$botman = BotManFactory::create([], $symfonyCache);
....
$botman->hears(
'survey',
function (BotMan $bot) {
$bot->startConversation(new OnBoardingConversation());
}
);
}
To create the OnBoardingConversation, just follow the documentation on create a conversation in botman
I'm trying to add twig-view in slim v4
In slim v3, we add twig-view in container
$container['view'] = function ($c) {
$view = new \Slim\Views\Twig('path/to/templates', [
'cache' => 'path/to/cache'
]);
// Instantiate and add Slim specific extension
$router = $c->get('router');
$uri = \Slim\Http\Uri::createFromEnvironment(new \Slim\Http\Environment($_SERVER));
$view->addExtension(new \Slim\Views\TwigExtension($router, $uri));
return $view;
};
but I can't add twig like that in slim v4
Update: Twig-View has reached a stable version and the docs are updated to address Slim 4 integration.
If you are still using an unstable version of Twig-View, please consider upgrading.
First, you need to add Twig-View package to your project:
composer require slim/twig-view
And assuming the following directory structure:
composer.json
cache/
public/
|--index.php
templates/
|--hello.twig
vendor/
|--autoload.php
The followings are two working examples:
If you use a container (which is optional according to Slim 4 docs), you can add Tiwg creation definition to the container and use it when required. (I'm using php-di/php-di in this example, but you can use any PSR compatible dependency container.)
index.php, using a container:
<?php
use DI\Container;
use Slim\Factory\AppFactory;
use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
require __DIR__ . '/../vendor/autoload.php';
// Create Container
$container = new Container();
AppFactory::setContainer($container);
// Set view in Container
$container->set('view', function() {
return Twig::create(__DIR__ . '/../templates',
['cache' => __DIR__ . '/../cache']);
});
// Create App
$app = AppFactory::create();
// Add Twig-View Middleware
$app->add(TwigMiddleware::createFromContainer($app));
// Example route
$app->get('/hello/{name}', function ($request, $response, $args) {
return $this->get('view')->render($response, 'hello.twig', [
'name' => $args['name']
]);
});
// Run the app
$app->run();
You can also skip the container creation, but in that case you need to create the Twig instance before trying to render a template.
index.php, without a container:
<?php
use Slim\Factory\AppFactory;
use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
require __DIR__ . '/../vendor/autoload.php';
// Create App
$app = AppFactory::create();
// Create Twig
$twig = Twig::create(__DIR__ . '/../templates',
['cache' => __DIR__ . '/../cache']);
// Add Twig-View Middleware
$app->add(TwigMiddleware::create($app, $twig));
// Example route
// Please note how $view is created from the request
$app->get('/hello/{name}', function ($request, $response, $args) {
$view = Twig::fromRequest($request);
return $view->render($response, 'hello.twig', [
'name' => $args['name']
]);
});
// Run the app
$app->run();
hello.twig:
Hello {{ name }}
Now try visiting /hello/slim4 in your browser and the output will be:
Hello slim4
SlimTwigView is at 3.0.0 beta (at least as of October 12, 2019), and some things have changed. The few online tutorials I've seen, as well as the official documentation no longer work.
TwigMiddleware no longer takes an instance of the $container as an argument, so you must first put Twig on the Container manually such as:
$container->set('view', function() {
// Of course put correct path to your views here
return new Twig('../views', ['cache' => false]);
});
You then you can add TwigMiddleware to your Slim App using the class' new createFromContainer method, like so:
$app->add(TwigMiddleware::createFromContainer($app));
// which is equivalent to:
// $app->add(TwigMiddleware::createFromContainer($app, 'view'));
At that point, you can render a Twig view like so:
$app->get('/', function (Request $request, Response $response, $args) {
return $this->get('view')->render($response, 'home.twig');
});
When using the Slim specific middleware, you now have access to the additional Twig extensions:
url_for
full_url_for
is_current_url
current_url
get_uri
Well! In my case I was using Slim 4.0 and Twig ^2.5. All I added to my code was
$container->set('view', function () use ($container) {
$view = new \Slim\Views\Twig(
__DIR__ .'/Templates'
, [ 'cache' => false ] //you can turn on caching by providing string path to cache or set to false
);
return $view;
});
I browsed the internet and didn't find much information on how to use any caching library with Slim framework 3.
Can anyone help me with this issue?
I use symfony/cache with Slim 3. You can use any other cache library, but I give an example setup for this specific library. And I should mention, this is actually independent of Slim or any other framework.
First you need to include this library in your project, I recommend using composer. I also will iinclude predis/predis to be able to use Redis adapter:
composer require symfony/cache predis/predis
Then I'll use Dependency Injection Container to setup cache pool to make it available to other objects which need to use caching features:
// If you created your project using slim skeleton app
// this should probably be placed in depndencies.php
$container['cache'] = function ($c) {
$config = [
'schema' => 'tcp',
'host' => 'localhost',
'port' => 6379,
// other options
];
$connection = new Predis\Client($config);
return new Symfony\Component\Cache\Adapter\RedisAdapter($connection);
}
Now you have a cache item pool in $container['cache'] which has methods defined in PSR-6.
Here is a sample code using it:
class SampleClass {
protected $cache;
public function __construct($cache) {
$this->cache = $cache;
}
public function doSomething() {
$item = $this->cache->getItem('unique-cache-key');
if ($item->isHit()) {
return 'I was previously called at ' . $item->get();
}
else {
$item->set(time());
$item->expiresAfter(3600);
$this->cache->save($item);
return 'I am being called for the first time, I will return results from cache for the next 3600 seconds.';
}
}
}
Now when you want to create new instance of SampleClass you should pass this cache item pool from the DIC, for example in a route callback:
$app->get('/foo', function (){
$bar = new SampleClass($this->get('cache'));
return $bar->doSomething();
});
$memcached = new \Memcached();
$memcached->addServer($cachedHost, $cachedPort);
$metadataCache = new \Doctrine\Common\Cache\MemcachedCache();
$metadataCache->setMemcached($memcached);
$queryCache = new \Doctrine\Common\Cache\MemcachedCache();
$queryCache->setMemcached($memcached);
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 :)
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();