I have a question regarding Slim configuration for the Cloud Foundry.
I'm using the latest build pack version and 'nginx' for my app.
$_ENV['SLIM_MODE'] = 'development';
//Slim App Instance
$app = new \Slim\Slim();
$app->configureMode('development', function () use ($app) {
$app->config(array(
'log.enable' => true,
'log.level' => \Slim\Log::DEBUG,
'debug' => true
));
});
$app->group('/v1', function () use ($app) {
$app->get('/hello/:name', function ($name) use ($app) {
$app->getLog()->info('###Some text###');
$response = $app->response();
$response['Content-Type'] = 'application/json';
$response->status(201);
$response->body(json_encode(['hello' => $name]));
});
});
Unable to see the following text in the Cloud Foundry logs.
Did I miss something?
Related
I am completelly new to Slim. I have used php for the last 3-4 years but I have always done everything from scratch. I want to learn this frameworks for some rest services I have to do.
I have followed a tutorial on the slim webpage to get a simple rest service working but I want to add a log system to see what is happening when something goes wrong or wathever.
This is what I have right know:
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../Slim/Slim.php';
\Slim\Slim::requisterAutoloader();
$application = new \Slim\App();
$logger = $application->log;
$logger->setEnabled(true);
$logger->setLevel(\Slim\Log::DEBUG);
$application->get(
'/hello/user',
function ()
{
GLOBAL $logger;
$logger->info("starting the handling function");
echo "<data>response</data>";
$logger->info("ending handling function");
});
$application->run();
?>
I also tried with monolog but I didn't get it working.
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
require '../vendor/autoload.php';
$logger = new \Flynsarmy\SlimMonolog\Log\MonologWriter(array(
'handlers' => array(
new \Monolog\Handler\StreamHandler('./logs/'.date('Y-m-d').'.log'),
),
));
$app = new Slim\App(array(
'log.writer' => $logger,
));
$app->get('/hello/{name}', function (Request $request, Response $response){
$name = $request->getAttribute('name');
$response->getBody()->write("Hello, $name");
//$app->log->writer("hola");
$this->logger->info("Slim-Skeleton '/' route");
return $response;
});
$app->run();
What I would really want is to have a daily log with warnings, debug, info... in the same file. Changing the file every day.
The slim-skeleton application shows how to integrate monolog.
Register the logger with the DI container:
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new \Monolog\Logger('test-app');
$logger->pushHandler(new Monolog\Handler\StreamHandler('php://stdout', \Monolog\Logger::DEBUG));
return $logger;
};
Use it in your closure:
$app->get('/hello/{name}', function (Request $request, Response $response){
$name = $request->getAttribute('name');
$response->getBody()->write("Hello, $name");
$this->get('logger')->info("Slim-Skeleton '/' route");
return $response;
});
The logs will out output to stdout, so you'll see the in your apache error log or if you're using the built-in PHP server, in the terminal.
http://www.slimframework.com/docs/tutorial/first-app.html shows using "plain old PHP" (Composer package is php-view and class is PhpRenderer) for templates as follows:
<?php
// Create app
$app = new \Slim\App();
// Get container
$container = $app->getContainer();
$container['view'] = new \Slim\Views\PhpRenderer("../templates/");
$app->get('/tickets', function (Request $request, Response $response) {
$this->logger->addInfo("Ticket list");
$mapper = new TicketMapper($this->db);
$tickets = $mapper->getTickets();
$response = $this->view->render($response, "tickets.phtml", ["tickets" => $tickets]);
return $response;
});
// Run app
$app->run();
The above page states using Twig for templates is basically the same, but http://www.slimframework.com/docs/features/templates.html shows a very different approach. Which is the correct way to use templates, and specifically how should I use Twig for templates?
<?php
// Create app
$app = new \Slim\App();
// Get container
$container = $app->getContainer();
// Register component on container
$container['view'] = function ($container) {
$view = new \Slim\Views\Twig('path/to/templates', [
'cache' => 'path/to/cache'
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$container['router'],
$container['request']->getUri()
));
return $view;
};
// Render Twig template in route
$app->get('/hello/{name}', function ($request, $response, $args) {
return $this->view->render($response, 'profile.html', [
'name' => $args['name']
]);
})->setName('profile');
// Run app
$app->run();
I have installed slim framework 3 and twig template following the composer.
When i call function http://localhost/elec/helloo/sandesh it displays Hello, Sandesh as followed on slim 3 documentation.
But when i try to call view page(inside templates folder).
It displays an error page Slim Application Error The application could not run because of the following error Error Description
Code Worked ( displays hello , {name} from function)
$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response) {
$name = $request->getAttribute('name');
$response->getBody()->write("Hello, $name");
return $response;
});
Code error ( displays error when called view page from function)
$settings = [
'settings' => [
'displayErrorDetails' => true,
],
];
$app = new Slim\App($settings);
// Get container
$container = $app->getContainer();
// Register component on container
$container['view'] = function ($container) {
return new \Slim\Views\PhpRenderer("templates/");
};
// Render Twig template in route
$app->get('/helloo/{name}', function ($request, $response, $args) {
return $this->view->render($response, 'view1.html', [
'name' => $args['name']
]);
})->setName('profile');
Path Detail
elec>
>>cache
>>templates
>>>view1.html
>>vender
>>.htaccess
>>composer.json
>>composer.lock
>>index.php
When passing the templates location, you have to provide a full path, (starting from the location of the running index.php file:
<?php
$container['view'] = function ($container) {
return new \Slim\Views\PhpRenderer(__DIR__ . "/../path/to/templates/");
};
try it out, and good luck.
Note: I'm using the same line but with Twig render:
<?php
$container['view'] = function ($container) {
return new \Slim\Views\Twig(__DIR__ . "/../path/to/templates/");
};
$app = new \Slim\App([
'settings' => [
'displayErrorDetails' => true,
]
]);
// Calling twigview from controller
$container = $app->getContainer();
// Register component on container
$container['view'] = function ($container) {
$view = new \Slim\Views\Twig('templates/views',[
'cache' => false,
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$container->router,
$container->request->getUri()
));
return $view;
};
$app->get('/home', function ($request, $response) {
return $this->view->render($response, 'home.twig');
});
I'm pretty new to the Silex Framework and I was wondering how to make a simple login (using SecurityServiceProvider) ajax request. Everything works well in my code (see below) but how can I change the html page returned for a boolean giving true or false wether the login worked or not.
app.php
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
// Register global error and exception handlers
ErrorHandler::register();
ExceptionHandler::register();
// Register service providers
$app->register(new Silex\Provider\DoctrineServiceProvider());
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__ . '/../views',
));
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->register(new Silex\Provider\SessionServiceProvider());
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'secured' => array(
'pattern' => '^/',
'anonymous' => true,
'logout' => array('logout_path' => '/admin/logout', 'invalidate_session' => true),
'form' => array('login_path' => 'login', 'check_path' => '/login_check'),
'users' => $app->share(function () use ($app) {
return new ski\DAO\MemberDAO($app['db']);
}),
),
),
));
// register services
$app['dao.member'] = $app->share(function ($app) {
return new ski\DAO\MemberDAO($app['db']);
});
routes.php
use Symfony\Component\HttpFoundation\Request;
// Home page
$app->get('/', function () use ($app) {
return $app['twig']->render('index.html.twig');
})->bind('home');
// TODO : never called
$app->post('/ajax/login/', function (Request $request) use ($app) {
// HERE : how to return if the login was performed well ?
return $app['security.last_error']($request);
})->bind('ajax_login');
$app->get('/login/', function (Request $request) use ($app) {
return $app['twig']->render('login.html.twig', array(
'error' => $app['security.last_error']($request),
'last_username' => $app['session']->get('_security.last_username'),
));
})->bind('login');
$app->get('/includes/header/', function () use ($app) {
return $app['twig']->render('header.html.twig');
})->bind('header');
and login.js
// Connexion
$(document).on('click', '#connexion_submit_button', function () {
// Connexion Ajax
var username = $('#connexion_pseudo').val();
var password = $('#connexion_password').val();
$.ajax({
type: 'POST',
url: '/ski/web/login_check',
data: '_username=' + username + '&_password=' + password,
beforeSend: function () {
$('#connexion_submit_button').html('Patientez...');
},
success: function (data) {
// TODO : generate custom animations if user is logged or not
console.log(data);
$('#connexion_submit_button').html('Connexion');
}
});
return false;
});
Any suggestions will be greatly appreciated !
And by the way, is there any good manners to do ajax in such frameworks ?
Silex uses Symfony's Security component under the hood and SecurityServiceProvider sets $app['dispatcher'] as event dispatcher to AuthenticationProviderManager. I guess this means that it'll fire events as listed in Security Component documentation.
The one interesting ones for you should be security.interactive_login and security.authentication.failure. Both fire an event where you have full access to the Request object where you can modify matching controller or do whatever you want by subscribing to events using EventSubscriberInterface.
Authentication success and failure handlers create and manage response. So you need custom authentication success and failure handlers if you want to make custom response.
Add handlers
MyAuthenticationFailureHandler
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler as BaseDefaultAuthenticationFailureHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class MyAuthenticationFailureHandler extends BaseDefaultAuthenticationFailureHandler
{
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) return new JsonResponse(['login' => false]);
return parent::onAuthenticationFailure($request, $exception);
}
}
MyAuthenticationSuccessHandler
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler as BaseDefaultAuthenticationSuccessHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class MyAuthenticationSuccessHandler extends BaseDefaultAuthenticationSuccessHandler
{
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) return new JsonResponse(['login' => true]);
return parent::onAuthenticationSuccess($request, $token);
}
}
and register them in security service provider
$app['security.authentication.success_handler.secured'] = $app->share(function () use ($app) {
$handler = new MyAuthenticationSuccessHandler(
$app['security.http_utils'],
$app['security.firewalls']['secured']['form'] //$options,
);
$handler->setProviderKey('secured');
return $handler;
});
$app['security.authentication.failure_handler.secured'] = $app->share(function () use ($app) {
return new MyAuthenticationFailureHandler(
$app,
$app['security.http_utils'],
$app['security.firewalls']['secured']['form'],
$app['logger']
);
});
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"
},