I'm not sure how I should be handling errors within Slim.
I am using a class with functions, e.g. login. When the user enters an incorrect password I'm unsure of how to give them to Slim so that they are able to be displayed to the user.
This is what I currently have:
Route:
$app->post('/submit', function () use ($app) {
$post = (object) $app->request->post();
if($app->user->login($post->log_mail, $post->log_pass)){
$app->setCookie('mail', $post->log_mail, '2 minutes');
$app->redirect('/inbox');
}else{
// redirect
// display errors set by the $app->user->login() function.
}
});
Class function:
public function login($username, $password){
if(!empty($username)){
if(!empty($password)){
if($this->numrows($username, $password)){
// login success
return true;
}else{die('credentials');}
}else{die('password');}
}else{die('username');}
}
How I make use of cookies:
$app = new \Slim\Slim(
[
'debug' => true,
'mode' => 'development',
'cookies.encrypt' => true,
'cookies.secret_key' => 'supersecret',
]);
$app->setCookie('something', 'something' '2 minutes');
Where I am currently using die(), I would like to set an actual error so that the Slim view is able to render it. I looked into Slim's Flash function but so far isn't working.
UPDATE
From the documentation:
If you use the session cookie middleware, you DO NOT need to start a
native PHP session.
Using \Slim\Middleware\SessionCookie, the code will be:
$app = new Slim();
$app->add(new \Slim\Middleware\SessionCookie(array(
'expires' => '20 minutes',
'path' => '/',
'domain' => null,
'secure' => false,
'httponly' => false,
'name' => 'slim_session',
'secret' => 'CHANGE_ME',
'cipher' => MCRYPT_RIJNDAEL_256,
'cipher_mode' => MCRYPT_MODE_CBC
)));
This is my working solution, without using \Slim\Middleware\SessionCookie:
index.php
<?php
require 'vendor/autoload.php';
session_start();
// Twig
$twigView = new \Slim\Views\Twig();
$twigView->parserExtensions = array(
new \Slim\Views\TwigExtension()
);
$app = new \Slim\Slim(
[
'debug' => true,
'mode' => 'development',
'cookies.encrypt' => true,
'cookies.secret_key' => 'supersecret',
'view' => $twigView
]);
$app->setCookie('something', 'something', '2 minutes');
class User {
private function numRows($username, $password){
//Your magic here
return true;
}
public function login($username, $password){
$app = \Slim\Slim::getInstance();
if(!empty($username)){
if(!empty($password)){
if($this->numrows($username, $password)){
// login success
return true;
}else{
$app->flash('error', 'credentials');
}
} else{
$app->flash('error', 'password');
}
} else{
$app->flash('error', 'username');
}
return false;
}
}
//Inject the new user
$app->user = new User();
$app->get('/', function () use ($app){
$flash = $app->view()->getData('flash');
$app->render('form.html');
})->name('Login');
$app->get('/inbox', function () use ($app){
echo "Login OK";
})->name('Inbox');
$app->post('/submit', function () use ($app) {
$post = (object) $app->request->post();
if($app->user->login($post->log_mail, $post->log_pass)){
$app->setCookie('mail', $post->log_mail, '2 minutes');
$app->redirect($app->urlFor('Inbox'));
}else{
// redirect
// display errors set by the $app->user->login() function.
$app->redirect($app->urlFor('Login'));
}
});
$app->run();
form.html
<html>
<head>
<title>Stackoverflow 33192640</title>
</head>
<body>
<form action="./submit" method="POST">
<input type="text" name="log_mail">
<input type="password" name="log_pass">
<button type="submit">Login</button>
</form>
{% if flash.error %}
Error: {{ flash.error }}
{% endif %}
</body>
</html>
OLD ANSWER
Your problem should be connected to the PHP session. You have to set it on top of your file:
session_start();
EDIT:
I misread the question, i think what you are looking for is a simply try catch structure defined your custom exception like this.
class LoginException extends Exception
{
public function __construct($message, $code = 0, Exception $previous = null) {
parent::__construct($message, $code, $previous);
}
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
With your code looking like this.
public function login($username, $password){
if(!empty($username)){
if(!empty($password)){
if($this->numrows($username, $password)){
// login success
return true;
}else{ throw new CustomException('Login error', 1);}
}else{throw new CustomException('password error', 2);}
}else{throw new CustomException('username error', 3);}
}
try {
if($app->user->login($post->log_mail, $post->log_pass)){
$app->setCookie('mail', $post->log_mail, '2 minutes');
$app->redirect('/inbox');
}
} catch (CustomException $e) {
// Handle the different codes
}
Here you define an exception, which you give a message and a code, this will represent your different error situation. Know you can handle them differently in the code, and when they occour you just throw the exception with the code which is relevant.
What you are looking for is invoking custom errors, as described on http://docs.slimframework.com/errors/500/.
Raising a custom error would look something like this. Where you could invoke templates with custom errors and so on. There for no need for the if else logic in the submit section.
$app->error(function (\Exception $e) use ($app) {
DoSomethingElse();
$app->render('mycustomerror.php');
});
Related
I'm using PHP on IIS, Phalcon framework. I have a login controller which I'm working on (yes, password isn't encrypted yet, but that's later) but I can't seem to get it to work.
I have a form action posting to signin/doSignin. This is a snippet of the SigninController.php:
public function doSigninAction(){
//$this->view->disable();
$user = User::findFirst([
'conditions' => 'email = :email: AND password = :password:'
, 'bind' => [
'email' => $this->request->getPost('email')
, 'password' => $this->request->getPost('password')
]
]);
if ($user){
echo 1;
return;
}
echo 2;
What results when I run this code is a blank page that simply reports:
Call to undefined method or service 'getDI'
Something somewhere is not lined up properly for the internals of phalcon, but I don't know what I need to check. When I change the above code to this, I get a proper rendering of the view, printing 1 for the user:
$user = new User()/*::findFirst([
'conditions' => 'email = :email: AND password = :password:'
, 'bind' => [
'email' => $this->request->getPost('email')
, 'password' => $this->request->getPost('password')
]
]);*/;
if ($user){
echo 1;
return;
}
echo 2;
My bootstrap is the following:
<?php
try {
set_include_path('c:/workspace/GIIAnalytics/app/views');
//Autoloader
$loader = new \Phalcon\Loader();
$loader->registerDirs([
'../app/controllers/'
, '../app/models/'
, '../app/config/'
]);
/** For MSSQL connections */
$loader->registerNamespaces([
"Twm\Db\Adapter\Pdo" => "../app/library/db/adapter/"
, "Twm\Db\Dialect" => "../app/library/db/dialect/"
]);
$loader->register();
//Dependancy Injection
$di = new \Phalcon\DI\FactoryDefault();
//Config
$configFile = __DIR__ . '/../app/config/config.json';
$config = json_decode ( file_get_contents ( $configFile ) );
$di->setShared('config',$config);
//MSSQL Database connection
$di->set("db", function() use ($di) {
//Database info
/** For MSSQL connections */
$mc = $di->getDI()->getShared('config')['db'];
$db = new Twm\Db\Adapter\Pdo\Mssql($mc);
return $db;
});
//View
$di->set('view', function(){
$view = new \Phalcon\Mvc\View();
$view->setViewsDir('../app/views');
/*
$view->registerEngines([
'.volt' => '\Phalcon\Mvc\View\Engine\Volt'
]);
*/
return $view;
});
// Router
$di->set('router',function(){
$router = new \Phalcon\Mvc\Router();
$router->mount(new Routes());
return $router;
});
// Session
$di->setShared('session', function() {
$session = new \Phalcon\Session\Adapter\Files();
$session->start();
return $session;
});
//Flash Data (temp data)
$di->set('flash', function() {
$flash = new \Phalcon\Flash\Session([
'error' => 'alert alert-danger'
, 'success' => 'alert alert-success'
, 'notice' => 'alert alert-info'
, 'warning' => 'alert alert-warning'
]);
return $flash;
});
//Metadata
$di['modelsMetadata'] = function() {
$metaData = new \Phalcon\Mvc\Model\MetaData\Memory([
'lifetime' => 86400
, 'prefix' => 'metaData'
]);
return $metaData;
};
// custom dispatcher overrides the default
$di->set('dispatcher', function() use ($di) {
$eventsManager = $di->getShared('eventsManager');
// Custom ACL class
$permission = new Permission();
// Listen for events from the $permission class
$eventsManager->attach('dispatch', $permission);
$dispatcher = new \Phalcon\Mvc\Dispatcher();
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
});
//Deploy the app
$app = new \Phalcon\Mvc\Application($di);
echo $app->handle()->getContent();
} catch(\Phalcon\Exception $e) {
echo $e->getmessage();
}
?>
How do I fix the dependency problem?
Found the issue. Phalcon is not good about giving you the line for invalid function calls. It was in my db constructor, where I'm doing $di->getDI this got changed (along with the config reader) to this snippet:
//MSSQL Database connection
$di->set("db", function() use ($di) {
//Database info
/** For MSSQL connections */
$mc = (array) $di->getShared('config')->db;
$db = new Twm\Db\Adapter\Pdo\Mssql($mc);
return $db;
});
My problem started right after I upgraded PHP to Version 7 and Phalcon to Version 3.
Problem
I m getting blank page, no error messages (Error is turned on), no `500 Internal server' error in console. The site used to work flawlessly before.
I have following controller IndexController.php
<?php
namespace RealEstate\Property\Controllers;
use \Phalcon\Mvc\Controller;
use \Phalcon\Mvc\View;
use RealEstate\Common\Models as CommonModels;
class IndexController extends Controller
{
public function initialize(){
//Code here
}
public function indexAction(){
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->setVar("total_properties", $this->utils->getTotalProperties());
$this->view->pick("index");
echo "HELLO WORLD";
}
}
The index action doesnot render anything, but yes HELLO WORLD is printed, so there seems there is no errors in code above that line.
My bootstrap index.php
<?php
namespace RealEstate;
use \Phalcon\Mvc\Application;
use \Phalcon\DI\FactoryDefault;
use \Phalcon\Loader;
use \Phalcon\Mvc\Router;
use \Phalcon\Mvc\View;
use \Phalcon\Mvc\Dispatcher;
use \Phalcon\Events\Manager as EventManager;
use \Phalcon\Assets\Manager as Assets;
use \Phalcon\Mvc\Url as UrlProvider;
use \Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;
use \Phalcon\Flash\Session as FlashSession;
use \Phalcon\Session\Adapter\Files as SessionAdapter;
use \Phalcon\Http\Response\Cookies;
//use \Phalcon\Session\Adapter\Files as Session;
class MyApplication extends Application
{
const DEFAULT_MODULE = "property";
private $prConfig;
/**
* Register the services here to make them general or register in the ModuleDefinition to make them module-specific
*/
protected function _registerServices()
{
try{
$config = include "../apps/config/config.php";
$di = new FactoryDefault();
$loader = new Loader();
/**
* We're a registering a set of directories taken from the configuration file
*/
$loader->registerDirs(
array(
__DIR__ . '/../apps/library/',
__DIR__ . '/../apps/plugins/'
)
);
$loader->registerNamespaces(array(
'RealEstate\Common\Plugins' => '../apps/plugins/',
'RealEstate\Common\Models' => '../apps/common/models/',
'RealEstate\Library\Pagination' => '../apps/library/',
'Facebook' => __DIR__.'/../apps/plugins/FacebookSDK/'
));
$loader->registerClasses(array(
"FacebookLib" => __DIR__.'/../apps/library/FacebookLib.php',
"Facebook" => __DIR__.'/../apps/plugins/FacebookSDK/autoload.php',
"MobileDetect" => __DIR__.'/../apps/library/MobileDetect.php'
));
$loader->register();
$di->set('config', $config, true);
//Register assets like CSS and JS
$di->set("assets",function(){
$assets = new Assets();
$cdnUrl = $this->cdnurl;
//Add CSS to collection. "true" means we're using base url for css path
$assets->collection('css')->addCss($cdnUrl.'assets/css/frontend/combined.css?v=44');
$assets->collection('footer')->addJs($cdnUrl.'assets/js/frontend/combined.js?v=44', false);
return $assets;
});
$this->prConfig = $config;
//register base url
$di->set('baseurl', function() use ($config){
$url = $config->application->baseUri;
return $url;
});
//register CDN url
$di->set('cdnurl', function() use ($config){
$url = $config->application->cdnUri;
return $url;
});
//Module Information
$di->set('appconfig', function() use ($config){
$appconfig = $config->application;
return $appconfig;
});
//Register URL helper
//set base url
$di->set('url', function() use ($config){
$url = new UrlProvider();
$url->setBaseUri($config->application->baseUri);
return $url;
});
//Register dependency for a database connection
$di->setShared('db', function() use ($config){
return new DbAdapter(array(
"host" => $config->database->host,
"username" => $config->database->username,
"password" => $config->database->password,
"dbname" => $config->database->dbname
));
});
//Register and start session
$di->setShared('session', function() {
$session = new SessionAdapter();
$session->start();
return $session;
});
//Register custom library Utilities, so that it is available through out the application
$di->setShared("utils",function(){
return new \Utilities();
});
//Registering a router
$di->set('router', function() use ($config){
$router = new Router(false);
$controller = "index";
/*Backend Routers Configuration Start*/
$modules = $config->modules;
$router->add('/', array(
'module' => 'property',
'namespace' => 'RealEstate\Property\Controllers\\',
'controller' => 'index',
'action' => 'index'
));
$router->add('/:int', array(
'module' => 'property',
'namespace' => 'RealEstate\Property\Controllers\\',
'controller' => 'index',
'action' => 'index',
'params' =>1
));
//other router settings
$router->notFound(array(
'module' => 'errors',
'namespace' => 'RealEstate\Errors\Controllers\\',
'controller' => 'index',
'action' => 'show404'
));
$router->removeExtraSlashes(true); //ignore trailing slash in urls
/*Backend Routers Configuration End*/
return $router;
});
$di->set('partials', function() {
$partials = new View();
$partials->setPartialsDir('../apps/common/views/');
return $partials;
});
$this->setDI($di);
}catch(\Phalcon\Exception $e){
echo get_class($e), ": ", $e->getMessage(), "\n";
echo " File=", $e->getFile(), "\n";
echo " Line=", $e->getLine(), "\n";
echo $e->getTraceAsString();
}
}
public function main()
{
try{
if (!extension_loaded('phalcon')) {
die("Phalcon extension is not installed or enabled. Please check with host");
}
$this->_registerServices();
if($this->prConfig->application->environment=="development" || $_GET["showerrors"]==1){
ini_set("display_errors","On");
error_reporting(E_ALL ^ E_NOTICE);
}
$arraytemp = json_decode(json_encode($this->prConfig->modules), true); //convert Config object to a simple array
//$this->utils->printr($arraytemp,1);
$keys = array_keys($arraytemp);
$array = array();
if(count($keys)>0){
foreach($keys as $module){
$array[$module]["className"] = "RealEstate\\".ucwords($module)."\Module";
$array[$module]["path"] = "../apps/modules/".$module."/Module.php";
}
}else{
die("The entries for modules not found.");
}
$this->registerModules($array);
echo $this->handle()->getContent();
}catch(\Phalcon\Exception $e){
echo get_class($e), ": ", $e->getMessage(), "\n";
echo " File=", $e->getFile(), "\n";
echo " Line=", $e->getLine(), "\n";
echo $e->getTraceAsString();
}
}
}
$application = new MyApplication();
$application->main();
Have followed This Link as well, but with not much help.
UPDATE
No errors on Server's error log
Thanks
I think I solved this issue. The issue was in Module.php, the issue was in method
public function registerServices($di)
{
try{
//Registering the view component
$di->set('view', function() {
$view = new View();
$view->setViewsDir('../apps/modules/'.$this->module_name.'/views/');
return $view;
});
}catch(\Phalcon\Exception $e){
echo get_class($e), ": ", $e->getMessage(), "\n";
echo " File=", $e->getFile(), "\n";
echo " Line=", $e->getLine(), "\n";
echo $e->getTraceAsString();
}
}
Where, $module_name is a property defined in class.
In above code $this->module_name was undefined. so I changed above method to -
public function registerServices($di)
{
$module = $this->module_name;
try{
//Registering the view component
$di->set('view', function() use($module) {
$view = new View();
$view->setViewsDir('../apps/modules/'.$module.'/views/');
return $view;
});
}catch(\Phalcon\Exception $e){
echo get_class($e), ": ", $e->getMessage(), "\n";
echo " File=", $e->getFile(), "\n";
echo " Line=", $e->getLine(), "\n";
echo $e->getTraceAsString();
}
}
I wondered why it was working in previous version of Phalcon
I'm using Socialite to get user information from facebook. All is going well but my redirect isn't working
Sub-routes
I read that it's not possible to do a redirect from a submethod, or
any method that's not in your routes.
But how else can i redirect the user after I logged them in?
My URL looks like this after the successfull facebook handshake
http://tmdb.app/auth/login/facebook?code=AQBTKNZIxbfdBruAJBqZ8xx9Qnz...
Code
class SocialController extends Controller {
public function login(Authenticate $authenticate, Request $request)
{
return $authenticate->execute($request->has('code'), $this);
}
public function userHasLoggedIn($data)
{
$user = User::where('provider_id', $data->id)->first();
if( !$user )
{
$user = User::create([
'name' => $data->name,
'email' => $data->email,
'provider' => 'facebook',
'provider_id' => $data->id
]);
}
// NOT WORKING!
return redirect('test');
}
}
Your login function should be handling the redirect.
I'm guessing execute returns $data if the user is sucessfully logged in and false if not.
class SocialController extends Controller {
public function login(Authenticate $authenticate, Request $request)
{
if($data = $authenticate->execute($request->has('code'), $this))
{
$user = User::where('provider_id', $data->id)->first();
// maybe delegate the user creation to another class/service?
if( !$user )
{
$user = User::create([
'name' => $data->name,
'email' => $data->email,
'provider' => 'facebook',
'provider_id' => $data->id
]);
}
return redirect('test');
}
return redirect('fail_view');
}
}
You can do it using PHP header function in Laravel sub method. I try it and works properly. Hope it can help you.
// You can using the following code
$url= url("about-laravel");
header("Location:" . $url);
exit;
// Or using the following code to redirect and keep set flash message
$result= $this->yourMethod(); // return redirect($this->route)->with('flash_message', 'I\'m Flash Message'); for TRUE or NULL for false
if( $result ){
return $result;
}
I have a webservice module configured with json strategy. I am making a login functionality where if the user is not logged return an error.
To test if the user is logged have an event dispatch on Module class
class Module {
public function onBootstrap(MvcEvent $e) {
(...)
$eventManager->attach(MvcEvent::EVENT_DISPATCH, array($this, 'mvcPreDispatch'), 1000);
}
public function mvcPreDispatch(MvcEvent $e) {
$authService = $e->getApplication()->getServiceManager()->get('auth');
if($authService->isAuthenticated()) {
return true;
}
$model = new JsonModel(array(
'errors' => array(
'httpCode' => $e->getResponse()->getStatusCode(),
'title' => $e->getResponse()->getReasonPhrase(),
'message' => 'Login necessário'
)
));
return $model;
}
}
How can I stop the request and display the error message on the screen?
Afaik you need to short circuit the event by returning a Response object or by calling stopPropagation(true) on the event. This will stop all other dispatch listeners from executing and return a response to the client.
You can try something like this:
public function mvcPreDispatch(MvcEvent $e)
{
$authService = $e->getApplication()->getServiceManager()->get('auth');
if($authService->isAuthenticated()) {
return true;
}
$model = new JsonModel(array(
'errors' => array(
'httpCode' => $e->getResponse()->getStatusCode(),
'title' => $e->getResponse()->getReasonPhrase(),
'message' => 'Login necessário'
)
));
$e->setViewModel($model);
$e->stopPropagation(true);
}
I've created a module to authenticate a user. Now, after login I go to the index action and the system tells me that the authentication is all working fine. But What I want is to print some more user details from the Users table. When I try:
print_r($this->getServiceLocator()->get('AuthService')->getAdapter()->getResultRowObject());
I get no result. What am I doing wrong?
Thanks for your help.
In my module.php I've the following code(snippet):
public function getServiceConfig()
{
return array(
'abstract_factories' => array(),
'aliases' => array(),
'factories' => array(
// Some more code here but removed for simplicity
// Autentication
'AuthService' => function ($sm) {
$adapter = $sm->get('master_db');
$dbAuthAdapter = new DbAuthAdapter ( $adapter, 'Users', 'email', 'password' );
$auth = new AuthenticationService();
$auth->setAdapter ( $dbAuthAdapter );
return $auth;
},
// Some more code here but removed for simplicity
}
In my IndexController.php I've the following (snippets):
public function indexAction()
{
if(!$this->getServiceLocator()->get('AuthService')->hasIdentity()){
return $this->redirect()->toUrl('login');
}
echo "hello, it works!";
exit;
}
public function loginAction(){
$form = $this->getServiceLocator()->get('LoginForm');
$viewModel = new ViewModel(array('form' =>
$form));
return $viewModel;
}
public function processAction(){
// Lots of code here
if($bcrypt->verify($loginData['password'], $userData->password))
{
$this->getAuthService()
->getAdapter()
->setIdentity($loginData['email'])
->setCredential($userData->password);
$result = $this->getAuthService()->authenticate();
}
// Lots of code here where I check if $result->isValid and route to the
// correct action
}
public function getAuthService() {
if(!isset($this->authservice)) {
$this->authservice = $this->getServiceLocator()->get('AuthService');
}
return $this->authservice;
}
Instead of refering to the authentication result object (which properly only exists in the authentication request) you can simply store user details in the authentication identity (#see http://framework.zend.com/manual/2.1/en/modules/zend.authentication.intro.html).
For your case you could also store user specific details right after the validation of the authentication result in the authentication storage:
if ($result->isValid()) {
//authentication success
$resultRow = $this->authService->getAdapter()->getResultRowObject();
$this->authService->getStorage()->write(array(
'id' => $resultRow->id,
'user_agent' => $request->getServer('HTTP_USER_AGENT'))
);
}
(This information was taken from this authentication tutorial http://samsonasik.wordpress.com/2013/05/29/zend-framework-2-working-with-authenticationservice-and-db-session-save-handler/)