So I am trying to find the best way to get PHP SLIM 3 to log PHP exceptions and errors to a Database table instead of a log. whats the best way to accomplish this? So far in the documentation recommends this.
Dependency:
$app = new \Slim\App();
$c = $app->getContainer();
$c['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) {
return $response->withStatus(500)
->withHeader('Content-Type', 'text/html')
->write('Something went wrong!');
};
};
What I am having trouble understanding is where do I call this and how could I connect this to log it into a database table that I created. Any Ideas?
Have you tried this for Slim 3?
$config = [
'settings' => [
'displayErrorDetails' => true,
'db' => [
'driver' => 'mysql',
'host' => 'localhost',
'username' => 'root',
'database' => 'test',
'password' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'flags' => [
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
],
]
],
];
$app = new \Slim\App($config);
$container = $app->getContainer();
$container[PDO::class] = function ($container) {
$settings = $container['settings']['db'];
$host = $settings['host'];
$dbname = $settings['database'];
$username = $settings['username'];
$password = $settings['password'];
$charset = $settings['charset'];
$flags = $settings['flags'];
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
return new PDO($dsn, $username, $password, $flags);
}
$container[PDO::class] = function ($container) {
$settings = $container['settings']['db'];
$host = $settings['host'];
$dbname = $settings['database'];
$username = $settings['username'];
$password = $settings['password'];
$charset = $settings['charset'];
$flags = $settings['flags'];
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
return new PDO($dsn, $username, $password, $flags);
};
$container ['errorHandler'] = function ($container ) {
$connection = container[PDO::class];
return function ($request, $response, $exception) use ($connection) {
$row = [
'message' => $exception->getMessage(),
];
$sql = "INSERT INTO error_logs SET message=:message";
$connection->prepare($sql)->execute($row);
return $response->withStatus(500)
->withHeader('Content-Type', 'text/html')
->write('Something went wrong!');
};
};
Related
I work with MYSQL database in slim framework. Its perfectly worked.
But going to connect Postgresql, It not connected as well.
Here is the sample Coding: (settings.php)
declare(strict_types=1);
use App\Application\Settings\Settings;
use App\Application\Settings\SettingsInterface;
use DI\ContainerBuilder;
use Monolog\Logger;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
SettingsInterface::class => function () {
return new Settings([
'displayErrorDetails' => true, // Should be set to false in production
'logError' => true,
'logErrorDetails' => true,
'logger' => [
'name' => 'slim-app',
'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
'level' => Logger::DEBUG,
],
"db" =>
[
'driver' => 'pgsql',
'host' => 'localhost',
'port' => '5433',
'database' => 'test_db',
'username' => 'postgres',
'password' => 'password',
'prefix' => '',
'schema' => 'public',
]
]);
}
]);
};
Here is the code : (dependencies.php)
<?php
declare(strict_types=1);
use App\Application\Settings\SettingsInterface;
use DI\ContainerBuilder;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
LoggerInterface::class => function (ContainerInterface $c) {
$settings = $c->get(SettingsInterface::class);
$loggerSettings = $settings->get('logger');
$logger = new Logger($loggerSettings['name']);
$processor = new UidProcessor();
$logger->pushProcessor($processor);
$handler = new StreamHandler($loggerSettings['path'], $loggerSettings['level']);
$logger->pushHandler($handler);
return $logger;
},
PDO::class => function (ContainerInterface $c)
{
$settings = $c->get(SettingsInterface::class);
$dbSettings = $settings->get("db");
$host = $dbSettings['host'];
$dbname = $dbSettings['database'];
$username = $dbSettings['username'];
$password = $dbSettings['password'];
$port = $dbSettings['port'];
$dsn = new PDO ("pgsql:host=$host;port=$port;dbname=$dbname");
return new PDO($dsn, $username, $password);
},
]);
};
Here checking for database connection : (routes.php)
<?php
declare(strict_types=1);
use App\Application\Actions\User\ListUsersAction;
use App\Application\Actions\User\ViewUserAction;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\App;
use Slim\Interfaces\RouteCollectorProxyInterface as Group;
return function (App $app) {
$app->options('/{routes:.*}', function (Request $request, Response $response) {
// CORS Pre-Flight OPTIONS Request Handler
return $response;
});
$app->get('/', function (Request $request, Response $response) {
$response->getBody()->write('Hello world!');
return $response;
});
$app->group('/users', function (Group $group)
{
$group->get('', ListUsersAction::class);
$group->get('/{id}', ViewUserAction::class);
});
$app->post('/db-select', function (Request $request, Response $response)
{
$db = $this->get(PDO::class);
$sth = $db->prepare("SELECT * FROM login");
$sth->execute();
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
$payload = json_encode($data);
$response->getBody()->write($payload);
return $response->withHeader('Content-Type', 'application/json');
});
};
If I run the command such as localhost:8000/db-select Its give me the following error:
{
"statusCode": 500,
"error": {
"type": "SERVER_ERROR",
"description": "SQLSTATE[08006] [7] fe_sendauth: no password supplied"
}
}
I worked the sample code for MYSQL it worked perfect.
What else missed for Postgresql connection?
Check extension for PostgreSQL is enabled
extension=pdo_pgsql
Also you may take help fromm this question
fe_sendauth: no password supplied error in postgresql + laravel
Here is a bug:
$dsn = new PDO ("pgsql:host=$host;port=$port;dbname=$dbname");
return new PDO($dsn, $username, $password);
Try this:
return new PDO("pgsql:host=$host;port=$port;dbname=$dbname", $username, $password);
I am having issues with the following part of my code using graphql-php libraries.
'resolve' =>function($value,$args,$context)
When I run the query:
"http://localhost:8080/index.php?query={certificate(id:"123ecd"){id}}"
I get the below listed message:
{"errors":[{"message":"Internal server error","category":"internal",
"locations":[{"line":1,"column":2}],"path":["certificate"]}],"data":{"certificate":null}}
Secondly when I run a nested query
"http://192.168.211.15:8080/index.php?query{certificates{id,products{id}}}"
I get the below listed response:
{"errors":[{"message":"Internal server error","category":"internal","locations":[{"line":1,"column":26}],"path":["certificates",0,"products"]}
"data":{"certificates":[{"id":"a023gavcx","status":"Valid","products":null}]}}
Below is my complete code:
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
class CertificateType extends ObjectType{
public function __construct(){
$config = [
'name' => 'Certificate',
'fields' => function() {
return [
'id' => [
'type' => Types::nonNull(Types::string()),
],
'number' => [
'type' => Types::int()
],
'first_issue_date' => [
'type' => Types::string()
],
'products' => [
'type' => Types::product(),
'resolve'=> function($value, $args, $context){
$pdo = $context['pdo'];
$cert_id = $value->id;
$result = $pdo->query("select * from products where cert_id = {$cert_id} ");
return $result->fetchObject() ?: null;
}
]
];
}
];
parent::__construct($config);
}
}
use GraphQL\Type\Definition\Type;
class Types extends Type{
protected static $typeInstances = [];
public static function certificate(){
return static::getInstance(CertificateType::class);
}
public static function product(){
return static::getInstance(ProductType::class);
}
protected static function getInstance($class, $arg = null){
if (!isset(static::$typeInstances[$class])) {
$type = new $class($arg);
static::$typeInstances[$class] = $type;
}
return static::$typeInstances[$class];
}
}
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
class ProductType extends ObjectType
{
public function __construct()
{
$config = [
'name' => 'Product',
'fields' => function() {
return [
'id' => [
'type' => Types::nonNull(Types::string()),
],
'primary_activity' => [
'type' => Types::string()
],
'trade_name' => [
'type' => Types::string()
],
];
},
];
parent::__construct($config);
}
}
require_once __DIR__ . '/../../../../autoload.php';
use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
define('BASE_URL', 'http://127.0.0.1:8080');
ini_set('display_errors', 0);
$debug = !empty($_GET['debug']);
if ($debug) {
$phpErrors = [];
set_error_handler(function($severity, $message, $file, $line) use (&$phpErrors) {
$phpErrors[] = new ErrorException($message, 0, $severity, $file, $line);
});
}
try {
$dbHost = 'localhost';
$dbName = '*******';
$dbUsername = 'root';
$dbPassword = '*********';
$pdo = new PDO("mysql:host={$dbHost};dbname={$dbName}", $dbUsername, $dbPassword);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$appContext = [
'pdo' => $pdo ];
if (isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false) {
$raw = file_get_contents('php://input') ?: '';
$data = json_decode($raw, true);
} else {
$data = $_REQUEST;
}
$data += ['query' => null, 'variables' => null];
if (null === $data['query']) {
$data['query'] = '{hello}';
}
require __DIR__ . '/types/CertificateType.php';
require __DIR__ . '/types/ProductType.php';
require __DIR__ . '/types/OrganizationType.php';
require __DIR__ . '/Types.php';
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'hello' => [
'description' => ' Hello world',
'type' => Types::string(),
'resolve' => function() {
return 'Hello World';
}
],
'certificate' => [
'type' => Types::listOf(Types::certificate()),
'description' => 'This is the certificate identification',
'args' => [
'id' => Types::string()],
'resolve' => function ($rootValue,$args,$context) {
$pdo = $context['pdo'];
$id = $args['id'];
return $pdo->query("SELECT * from certificates where id ={$id}");
return $data->fetchObject() ?: null;
}
],
'certificates' => [
'type' => Types::listOf(Types::certificate()),
'resolve' => function($rootValue, $args, $context) {
$pdo = $context['pdo'];
$result = $pdo->query("select * from certificates order by id limit 10");
return $result->fetchAll(PDO::FETCH_OBJ);
}
],
]
]);
$schema = new Schema([
'query' => $queryType
]);
$result = GraphQL::execute(
$schema,
$data['query'],
null,
$appContext,
(array) $data['variables']
);
if ($debug && !empty($phpErrors)) {
$result['extensions']['phpErrors'] = array_map(
['GraphQL\Error\FormattedError', 'createFromPHPError'],
$phpErrors
);
}
$httpStatus = 200;
} catch (\Exception $error) {
// Handling Exception
// *************************************
$httpStatus = 500;
if (!empty($_GET['debug'])) {
$result['extensions']['exception'] = FormattedError::createFromException($error);
} else {
$result['errors'] = [FormattedError::create('Unexpected Error')];
}
}
header('Content-Type: application/json', true, $httpStatus);
echo json_encode($result);
Can somebody help me resolve these issues. Thanks in advance
The Zend\Session Save Handler tutorial gives an example for DbTableGateway in which they create a TableGateway with an undefined $adapter variable. I want to use the handler to tie the Session Manager (from the previous page of the tutorial) to my session storage table in my database. How can I do this?
I guess the code should look something like this?
class Module implements AutoloaderProviderInterface, ConfigProviderInterface
{
public function onBootstrap(MvcEvent $e) {
$eventManager = $e->getApplication()->getEventManager();
// create the session manager
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$this->bootstrapSession($e);
}
public function bootstrapSession($e)
{
$session = $e->getApplication()
->getServiceManager()
->get('Zend\Session\SessionManager');
$tableGateway = new TableGateway('session', $adapter); // somehow define this somewhere?
$saveHandler = new DbTableGateway($tableGateway, new DbTableGatewayOptions());
$session->setSaveHandler($saveHandler);
$session->start();
$container = new Container('initialized');
if (!isset($container->init)) {
$serviceManager = $e->getApplication()->getServiceManager();
$request = $serviceManager->get('Request');
$session->regenerateId(true);
$container->init = 1;
$container->remoteAddr = $request->getServer()->get('REMOTE_ADDR');
$container->httpUserAgent = $request->getServer()->get('HTTP_USER_AGENT');
$config = $serviceManager->get('Config');
if (!isset($config['session'])) {
return;
}
$sessionConfig = $config['session'];
if (isset($sessionConfig['validators'])) {
$chain = $session->getValidatorChain();
foreach ($sessionConfig['validators'] as $validator) {
switch ($validator) {
case 'Zend\Session\Validator\HttpUserAgent':
$validator = new $validator($container->httpUserAgent);
break;
case 'Zend\Session\Validator\RemoteAddr':
$validator = new $validator($container->remoteAddr);
break;
default:
$validator = new $validator();
}
$chain->attach('session.validate', array($validator, 'isValid'));
}
}
}
}
public function getServiceConfig()
{
return array(
'factories' => array(
'Zend\Session\SessionManager' => function ($sm) {
$config = $sm->get('config');
if (isset($config['session'])) {
$session = $config['session'];
$sessionConfig = null;
if (isset($session['config'])) {
$class = isset($session['config']['class']) ? $session['config']['class'] : 'Zend\Session\Config\SessionConfig';
$options = isset($session['config']['options']) ? $session['config']['options'] : array();
$sessionConfig = new $class();
$sessionConfig->setOptions($options);
}
$sessionStorage = null;
if (isset($session['storage'])) {
$class = $session['storage'];
$sessionStorage = new $class();
}
$sessionSaveHandler = null;
if (isset($session['save_handler'])) {
// class should be fetched from service manager since it will require constructor arguments
$sessionSaveHandler = $sm->get($session['save_handler']);
}
$sessionManager = new SessionManager($sessionConfig, $sessionStorage, $sessionSaveHandler);
} else {
$sessionManager = new SessionManager();
}
Container::setDefaultManager($sessionManager);
return $sessionManager;
},
),
);
}
/***************************************************************************************************
* Returns the location of the module.config.php file. This function is used by the Zend Framework
* underneath the hood.
***************************************************************************************************/
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
/***************************************************************************************************
* Returns the Zend StandardAutoLoader which contains the directory structure of the module source
* folder.
***************************************************************************************************/
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
I have this code for save a session to a db table.
'service_manager' => array(
'factories' => array(
'Zend\Session\SessionManager' => function (\Zend\ServiceManager\ServiceManager $sm) {
$sessionConfig = new \Zend\Session\Config\SessionConfig();
$sessionConfig->setOptions(
array(
'use_cookies' => true,
'name' => 'ed2',
'gc_maxlifetime' => 1728000
)
);
/* #var $adapter \Zend\Db\Adapter\Adapter */
$adapter = $sm->get('Zend\Db\Adapter\Adapter');
$tableGateway = new \Zend\Db\TableGateway\TableGateway('session', $adapter);
$saveHandler = new \Common\Session\SaveHandler\DbTableGateway(
$tableGateway,
new \Zend\Session\SaveHandler\DbTableGatewayOptions()
);
$sessionManager = new \Zend\Session\SessionManager($sessionConfig);
$sessionManager->setSaveHandler($saveHandler);
$sessionManager->start();
return $sessionManager;
},
)
)
Config for db
'db' => array(
'driver' => 'Pdo_Mysql',
'database' => 'release',
'username' => 'username',
'password' => 'password',
'hostname' => '127.0.0.1',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
)
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => function (\Zend\ServiceManager\ServiceManager $serviceManager) {
$adapterFactory = new Zend\Db\Adapter\AdapterServiceFactory();
$adapter = $adapterFactory->createService($serviceManager);
Zend\Db\TableGateway\Feature\GlobalAdapterFeature::setStaticAdapter($adapter);
return $adapter;
}
)
)
The answer turned out to be like newage's answer:
Since newage edited his answer to include the db adapter, I've accepted it as the right answer. The rest of this is just my implementation:
You can remove all the TableGateway and savehandler logic from the bootstrapSession method and put it in the getServiceConfig method.
Add the definition for Adapter to the 'factories' array in getServiceConfig, then modify the 'Zend\Session\SessionManager' function to include the Adapter, TableGateway, and save handler. This is what the new getServiceConfig would look like:
public function getServiceConfig()
{
return array(
'factories' => array(
// New code here
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
// New code here
'Zend\Session\SessionManager' => function ($sm) {
$config = $sm->get('config');
if (isset($config['session'])) {
$session = $config['session'];
$sessionConfig = null;
if (isset($session['config'])) {
$class = isset($session['config']['class']) ? $session['config']['class'] : 'Zend\Session\Config\SessionConfig';
$options = isset($session['config']['options']) ? $session['config']['options'] : array();
$sessionConfig = new $class();
$sessionConfig->setOptions($options);
}
$sessionStorage = null;
if (isset($session['storage'])) {
$class = $session['storage'];
$sessionStorage = new $class();
}
$sessionSaveHandler = null;
if (isset($session['save_handler'])) {
// class should be fetched from service manager since it will require constructor arguments
$sessionSaveHandler = $sm->get($session['save_handler']);
}
$sessionManager = new SessionManager();
}
// New code here
/* #var $adapter \Zend\Db\Adapter\Adapter */
$adapter = $sm->get('Zend\Db\Adapter\Adapter');
$tableGateway = new TableGateway('mytablename', $adapter);
$saveHandler = new DbTableGateway($tableGateway, new DbTableGatewayOptions());
$sessionManager->setSaveHandler($saveHandler);
// New code here
Container::setDefaultManager($sessionManager);
return $sessionManager;
},
),
);
}
Then add the database connection info to the module's config file:
return array(
// ...
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=mydbname;host=mydbhost;port=xxxx',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'dbusername',
'password' => 'dbpassword',
),
);
I am using Silex PHP framework.
I have two files, the first is app.php:
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ParameterBag;
use Silex\ControllerProviderInterface;
$app = new Silex\Application();
$app['debug'] = true;
$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
'db.options' => array(
'driver' => 'pdo_mysql',
'host' => 'localhost',
'dbname' => 'testapp',
'user' => 'root',
'password' => 'turoke55u',
),
));
$app->before(function (Request $request) {
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
$data = json_decode($request->getContent(), true);
$request->request->replace(is_array($data) ? $data : array());
}
});
$app->mount('/', include 'login.php');
$app->run();
And the second is login.php:
<?php
use Symfony\Component\HttpFoundation\Request;
$login = $app['controllers_factory'];
$login->post('/apiv1/user/login', function (Request $request) use ($login) {
$userinfo = array(
'email' => $request->request->get('email'),
'mode' => $request->request->get('mode'),
'password' => $request->request->get('password'),
);
$passwordcoding = sha1($userinfo['email']."66643asd");
$emailverification = "SELECT email,password FROM user WHERE email='".$userinfo['email']."'";
$selectemail = $login['db']->$fetchAll('SELECT * FROM user');
var_dump($emailverification);
});
return $login;
When I run the select on db I get this error:
Cannot use object of type Silex\ControllerCollection as array in /mnt/hgfs/Share_Folder/frontend/src/login.php on line 13
Does someone know a solution for that issue?
I have a second question, why if I change in app.php this:
$app->mount('/', include 'login.php');
To this:
$app->mount('/apiv1/user/login', include 'login.php');
And in login.php:
$login->post('/',
Like in the Silex documentation, the framework doesn't work?
You have a silly error in your function signature:
<?php
use Symfony\Component\HttpFoundation\Request;
$login = $app['controllers_factory'];
$login->post('/apiv1/user/login', function (Request $request) use ($login) {
// ^^^^^^^^
// this should be $app, not $login as this function is using the container
$userinfo = array(
'email' => $request->request->get('email'),
'mode' => $request->request->get('mode'),
'password' => $request->request->get('password'),
);
$passwordcoding = sha1($userinfo['email']."66643asd");
$emailverification = "SELECT email,password FROM user WHERE email='".$userinfo['email']."'";
// here you're using the $login as the container and it's a controller collection, so in the signature use the container not the $login
// vvvvvvvvvvvv
$selectemail = $login['db']->$fetchAll('SELECT * FROM user');
var_dump($emailverification);
});
return $login;
As for the second doubt you should elaborate more on "the framework doesn't work", explain the symptoms and the output (if error which error?)
Following is my cake console code to generate database schema. I have to manage multiple database schema migration. When I am calling generateDb function it creates a master schema after that i am switching database connection to client database, but client schema is not generating. its again generating master schema.
class HelloShell extends AppShell {
public $uses = array('ClientDbdetail');
public function generateDb() {
$runCommand = shell_exec(APP.'Console/cake schema generate -f master');
if ($runCommand) {
$sessionArray = $this->ClientDbdetail->find('first', array('recursive' => -1));
$this->__switchDb($sessionArray['ClientDbdetail']);
shell_exec(APP.'Console/cake schema generate -f client');
$this->out('Schema generated');
} else {
$this->out('Schema not generated');
}
}
private function __switchDb(array $userDetail) {
$username = 'default';
$settings = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'port' => 3306,
'login' => $userDetail['user_dbuser'],
'password' => $userDetail['user_dbpwd'],
'database' => $userDetail['user_dbname'],
'prefix' => ''
);
ConnectionManager::drop($username);
ConnectionManager::create($username, $settings);
ConnectionManager::getDataSource($username);
}
}
From CakePHP book there is an example.
public $connection = 'default';
public function before($event = array()) {
$db = ConnectionManager::getDataSource($this->connection);
$db->cacheSources = false;
return true;
}
and after :
public function before($event = array()) {
$articles = ClassRegistry::init('Articles', array(
'ds' => $this->connection
));
// Do things with articles.
}
see this : http://book.cakephp.org/2.0/en/console-and-shells/schema-management-and-migrations.html