How to Bootstrap Sessions in Zend Framework 2 - php

What is the best way to go about getting sessions up and running in Zend Framework 2? I've tried setting session_start() in my index.php file but then that gets run before any autoloaders have been bootstrapped, leading to incomplete objects sitting in my sessions.
In ZF1 you could have it initialize sessions by adding some options into the config, but I'm at a loss on how to do this in ZF2.

If i understand you correctly, all you wanna do is have your session working properly in your modules? Assuming that's correct there are two single steps.
1) Create the config: module.config.php
return array(
'session' => array(
'remember_me_seconds' => 2419200,
'use_cookies' => true,
'cookie_httponly' => true,
),
);
2) Start your Session: Module.php
use Zend\Session\Config\SessionConfig;
use Zend\Session\SessionManager;
use Zend\Session\Container;
use Zend\EventManager\EventInterface;
public function onBootstrap(EventInterface $evm)
{
$config = $evm->getApplication()
->getServiceManager()
->get('Configuration');
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session']);
$sessionManager = new SessionManager($sessionConfig);
$sessionManager->start();
/**
* Optional: If you later want to use namespaces, you can already store the
* Manager in the shared (static) Container (=namespace) field
*/
Container::setDefaultManager($sessionManager);
}
Find more options in the documentation of \Zend\Session\Config\SessionConfig
If you want to store cookies too, then please see this Question. Credits to Andreas Linden for his initial answer - I'm simply copy pasting his.

Related

ZF2 project stops working when is cloned to local server

I would like to know why when I clone my ZF2 project to a local machine to do some testing it completly stops working.
In my local machine I have two subfolders, one with a cakePHP project and the other with the ZF2 I've cloned.
The cakePHP project is working fine since it was there first, but the ZF2, when I try to access to the public folder it prints me:
{"error":"Something went wrong"}
A really generic error... I have no clue about what is going on.
I've tried some general debug attemps like
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
with no success at all, I've also checked the .htaccess RewriteBase directive to match my subfolder and the DB configuration is done too.
I have researched a bit in the project and the file which displays the error is module/RestfulV2_2/Module.php (Reading the README.md I've discovered is part of ZF2 Restful Module Skeleton):
/**
* #param MvcEvent $e
* #return null|\Zend\Http\PhpEnvironment\Response
*/
public function errorProcess(MvcEvent $e)
{
/** #var \Zend\Di\Di $di */
$di = $e->getApplication()->getServiceManager()->get('di');
$eventParams = $e->getParams();
/** #var array $configuration */
$configuration = $e->getApplication()->getConfig();
$vars = array();
if (isset($eventParams['exception'])) {
/** #var \Exception $exception */
$exception = $eventParams['exception'];
if ($configuration['errors']['show_exceptions']['message']) {
$vars['error-message'] = $exception->getMessage();
}
if ($configuration['errors']['show_exceptions']['trace']) {
$vars['error-trace'] = $exception->getTrace();
}
}
if (empty($vars)) {
$vars['error'] = 'Something went wrong';
}
/** #var PostProcessor\AbstractPostProcessor $postProcessor */
$postProcessor = $di->get(
$configuration['errors']['post_processor'],
array('vars' => $vars, 'response' => $e->getResponse())
);
$postProcessor->process();
if (
$eventParams['error'] === \Zend\Mvc\Application::ERROR_CONTROLLER_NOT_FOUND ||
$eventParams['error'] === \Zend\Mvc\Application::ERROR_ROUTER_NO_MATCH
) {
$e->getResponse()->setStatusCode(\Zend\Http\PhpEnvironment\Response::STATUS_CODE_501);
} else {
$e->getResponse()->setStatusCode(\Zend\Http\PhpEnvironment\Response::STATUS_CODE_500);
}
$e->stopPropagation();
return $postProcessor->getResponse();
}
The line which is calling the error in my index.php is:
Zend\Mvc\Application::init(require 'config/application.config.php')- run();
And the only line I found where the error function is called some way is this one in my modele.php :
$sharedEvents->attach('Zend\Mvc\Application', MvcEvent::EVENT_DISPATCH_ERROR, array($this, 'errorProcess'), 999);
Can you help me to solve this? I'm inexperienced with ZF2 but I know that with cakePHP to make it work you need to clear the cache folder. Is there any similar process in ZF2? Should I virtualize two servers to avoid conflics?
Thank you in advance.
EDIT : I've already made virtual hosts to avoid any possible conflict between my two frameworks but the error output is still the same.
EDIT2 : Here is my application.config.php file:
return array(
// This should be an array of module namespaces used in the application.
'modules' => array(
'Restful',
'MvlabsSnappy',
'Qrcode',
'Application',
'RestfulV2',
'RestfulV2_2'
),
// These are various options for the listeners attached to the ModuleManager
'module_listener_options' => array(
// This should be an array of paths in which modules reside.
// If a string key is provided, the listener will consider that a module
// namespace, the value of that key the specific path to that module's
// Module class.
'module_paths' => array(
'./module',
'./vendor',
),
// An array of paths from which to glob configuration files after
// modules are loaded. These effectively override configuration
// provided by modules themselves. Paths may use GLOB_BRACE notation.
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
// Whether or not to enable a configuration cache.
// If enabled, the merged configuration will be cached and used in
// subsequent requests.
//'config_cache_enabled' => $booleanValue,
// The key used to create the configuration cache file name.
//'config_cache_key' => $stringKey,
// Whether or not to enable a module class map cache.
// If enabled, creates a module class map cache which will be used
// by in future requests, to reduce the autoloading process.
//'module_map_cache_enabled' => $booleanValue,
// The key used to create the class map cache file name.
//'module_map_cache_key' => $stringKey,
// The path in which to cache merged configuration.
//'cache_dir' => $stringPath,
// Whether or not to enable modules dependency checking.
// Enabled by default, prevents usage of modules that depend on other modules
// that weren't loaded.
// 'check_dependencies' => true,
),
// Used to create an own service manager. May contain one or more child arrays.
//'service_listener_options' => array(
// array(
// 'service_manager' => $stringServiceManagerName,
// 'config_key' => $stringConfigKey,
// 'interface' => $stringOptionalInterface,
// 'method' => $stringRequiredMethodName,
// ),
// )
// Initial configuration with which to seed the ServiceManager.
// Should be compatible with Zend\ServiceManager\Config.
// 'service_manager' => array(),
);
First I would open index.php or whatever used as initial file (DirectoryIndex) and temporarily completely replace whole its content with something very base and simple, for example just these two lines:
<?php
phpinfo();
And then make sure that it started to work after that - with that simple code which just displays your php configuration. So we'll find out that there is no error in server configurations, permissions and etc. and nothing prevents your script from run.
Then I would do the same at your old project location just to get phpinfo() from that place too and waste some time trying to compare them. Maybe you missed something important and you'll now see it.
If no - next step I would check your DB connectivity if your project uses any DB... also with some very simple commands like connect, bind, ....
And finally I'd try to restore original project content step by step from its begin, and look at which step it will fail. It doesn't matter that there maybe no any output - you may put echo __LINE__ . ' works!<br/>'; between blocks, so your index.php will look like:
<?php
// original code block 1
echo __LINE__ . ' works!<br/>';
// original code block 2
echo __LINE__ . ' works!<br/>';
And you'll see in browser where it fails.
This is a very base description of my debug principals, but hope it will help.
The error could be anything. However, assuming the posted code is executed, it will suppress an error message without the correct configuration.
Try adding the following config to local.config.php.
return [
'errors'=> [
'show_exceptions' => [
'message' => true,
'trace' => true
],
],
];
If an exception is being thrown and that listener is catching it, then the $eventParams is something you should debug.

Proper way to inject dynamic configuration into configuration array

I'm wondering what is the best way to inject dynamic configuration(retrieved from db for instance) into configuration array in Zend Framework 2? In Module.php I have:
public function onBootstrap(MvcEvent $e) {
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$eventManager->attach('route', array($this, 'mergeDynamicConfig'));
}
public function mergeDynamicConfig(EventInterface $e) {
$application = $e->getApplication();
$sm = $application->getServiceManager();
$configurationTable = $sm->get('DynamicConfiguration\Model\Table\ConfigurationTable');
$dynamicConfig = $configurationTable->fetchAllConfig();
//Configuration array from db
//Array
//(
// [config] => 'Test1',
// [config2] => 'Test2',
// [config3] => 'Test3',
//)
//What to do here?
//I want to use the configurations above like $sm->get('Config')['dynamic_config']['config3'];
}
There is a section in the documentation that explains how to manipulate the merged configuration using the specific event ModuleEvent::EVENT_MERGE_CONFIG
Zend\ModuleManager\Listener\ConfigListener triggers a special event, Zend\ModuleManager\ModuleEvent::EVENT_MERGE_CONFIG, after merging all configuration, but prior to it being passed to the ServiceManager. By listening to this event, you can inspect the merged configuration and manipulate it.
The problem with this is that the service manager is not available at this point as the listener's event is one of the first events triggered by the module manager at priority 1000).
This means that you cannot execute your query and merge the config prior to the configuration being passed to the service manager, you would need to do so after.
Perhaps I have misunderstood your requirements, however I would approach this differently.
You could replace any calls where you need config $serviceManager->get('config') with $serviceManager->get('MyApplicationConfig'); which would be you own configuration service that uses the merged application config and then adds to it.
For example, you could register this configuration service in module.config.php.
return [
'service_manager' => [
'factories' => [
'MyApplicationConfig' => 'MyApplicationConfig\Factory\MyApplicationConfigFactory',
]
],
];
And create a factory to do the loading of merged module configuration, making any database calls or caching etc.
class MyApplicationConfigFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sm)
{
$config = $sm->get('config');
$dbConfig = $this->getDatabaseConfigAsArray($sm);
return array_replace_recursive($config, $dbConfig);
}
protected function getDatabaseConfigAsArray(ServiceLocatorInterface $sm)
{}
}
You also have the added benefit that the service is lazy loaded.
I would not use this approuch, for a few reasons.
Putting SQL queries in your Module.php means that they will get executed on EVERY request for every user thus making your application slow, very slow.
If your database is compromised all the config will be stolen as well.
Solution would be to move all the config in your config/autoload/my_custom_config.local.php via array with keys. From there you can always load it without making a single database request. It will be way faster and secure, because the file will be outside your root folder and hacking a server is always alot harder than hacking a database.
If you still want to allow users to eit the options you can simply include the file in an action and show it with a foreach for example. To save the information you can do this:
file_put_contents("my_custom_config.local.php", '<?php return ' . var_export($config, true).';');
One other plus is that if you load your config the way discribe above you can also retrive the config like you want via $sm->get('Config')['dynamic_config']['config3']

Using FirePHP with Zend Framework 2

I'm trying to use FirePHP with Zend Framework 2, but there seems to be something missing. Here's the basic code I'm trying to run:
$writer = new Zend\Log\Writer\FirePhp();
$logger = new Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('FirePHP logging enabled');
The error I get is "FirePHP Class not found". I was initially puzzled because I do have a FirePhp class in my Zend/Log/Writer folder. But then I saw that the class constructor requires a FirePhp\FirePhpInterface object. So I checked the Zend/Log/Writer/FirePhp folder and there's a FirePhpBridge class in there that implements FirePhpInterface, but it also requires a FirePHP instance in the constructor. I don't have any FirePHP.php file in my Zend/Log/Writer/FirePhp folder. Am I supposed to get this from somewhere else?
Update
I now have managed to get FirePHP working, but I'm trying to figure out how to do it in a clean way so this works. The only way I've gotten it to work is putting it in the root directory of my project and doing the following:
include_once('FirePHP.php');
$writer = new Zend\Log\Writer\FirePhp(new Zend\Log\Writer\FirePhp\FirePhpBridge(FirePHP::getInstance(true)));
$logger = new Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('FirePHP logging enabled');
I assume that normally I should be able to create a writer like so:
$writer = new Zend\Log\Writer\FirePhp();
However, where this goes wrong I believe is in the getFirePhp() function of the Zend\Log\Writer\FirePhp class. The class does this:
if (!$this->firephp instanceof FirePhp\FirePhpInterface
&& !class_exists('FirePHP')
) {
// No FirePHP instance, and no way to create one
throw new Exception\RuntimeException('FirePHP Class not found');
}
// Remember: class names in strings are absolute; thus the class_exists
// here references the canonical name for the FirePHP class
if (!$this->firephp instanceof FirePhp\FirePhpInterface
&& class_exists('FirePHP')
) {
// FirePHPService is an alias for FirePHP; otherwise the class
// names would clash in this file on this line.
$this->setFirePhp(new FirePhp\FirePhpBridge(new FirePHPService()));
}
This is where I get lost as to how I'm supposed to set things up so that this class_exists('FirePHP') call finds the right class and new FirePHPService() also works properly.
First you should add this code to Module.php of your module
return array(
//...
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
);
and here content of autoload_classmap.php
<?php
return array(
'FirePHP' => realpath(APPLICATION_PATH . '/vendor/FirePHP').'/FirePHP.php',
);
FirePHP.php(renamed from FirePHP.class.php) downloaded from official site.
then you can write below code in any place of your module and it will work
use Zend\Log\Writer\FirePhp;
use Zend\Log\Logger;
$writer = new FirePhp();
$logger = new Logger();
$logger->addWriter($writer);
$logger->info("hi");
Am I supposed to get this from somewhere else?
Yes, you need to get FirePHP into your project and autoloading.
If you're using composer (and I recommend that you do), just add:
"firephp/firephp-core" : "dev-master"
(or similar) in your composer.json and update. If you're not using composer, you should grab the firephp libs, and let your autoloader know about them.

Zend Cache Backend Static Remove/Clean

I'm trying to implement a cache using Zend Cache. I use the following code to initialize the caches.
$tagCache = Zend_Cache::factory('Core',
'File',
array('automatic_serialization' => true),
array('cache_dir' => $cfg['instdir']. 'Cache_dir'));
$cache = Zend_Cache::factory('Capture',
'Static',
array(),
array('index_filename' => 'index',
'public_dir' => $cfg['instdir'] . 'Cached',
'tag_cache' => $tagCache));
I use the following code to start caching:
$id = bin2hex($_SERVER["REQUEST_URI"]);
$cache->start($id, array());
The cache files are generated but I can't delete them using the remove() method (the cache doesn't refresh):
$cache->remove($id); // id generated like above from the REQUEST_URI of the page I want to refresh
What am I doing wrong ?
Thanks.
$cache->remove() is a proxy to the backend's remove() method. In this case you are using the Static backend and so we look in there to find out what's happening.
My reading of that method leads me to believe that the $id parameter to remove has to be a filename, so:
$cache->remove('index');
will work.
The more usual way to clear a cache is to use the clean() method though. See the manual.

Calling session_write_close() before Zend_Session::start() in the bootstrap causes an error

So I need access to an existing Session from Zend. Don't ask why, I don't like to talk about it. Anyway, I've gotten as far as discovering that I can access it from the bootstrap before I initialize my session. I have the following code in place to attempt to pull it out and transfer it over:
protected function _initSession() {
session_start();
$values = $_SESSION;
session_write_close();
$db = Zend_Db::factory('Pdo_Mysql', array(
'host' =>'localhost',
'username' => 'uname',
'password' => '******',
'dbname' => 'dbname'
));
Zend_Db_Table_Abstract::setDefaultAdapter($db);
$sessionConfig = array(
'name' => 'Sessions',
'primary' => 'sessionID',
'modifiedColumn' => 'lastModifiedTime',
'dataColumn' => 'data',
'lifetimeColumn' => 'lifetime'
);
$saveHandler = new Zend_Session_SaveHandler_DbTable($sessionConfig);
Zend_Session::setSaveHandler($saveHandler);
Zend_Session::start();
$old = new Zend_Session_Namespace('OLD');
$old->values = $values;
}
It's choking when it hits Zend_Session::start(), claiming that a session has already been started. But I've called session_write_close() to close the session and as far as I can tell from my google-fu there's nothing wrong with restarting a previously cosed session. So why is it choking? Is it something specific to ZF? Is there something more I need to do to close the session? What gives?
You just can't.
Foremost, as described in the ZF documentation :
Do not use PHP's ยป session_start() function directly. If you use session_start() directly, and then start using Zend_Session_Namespace, an exception will be thrown by Zend_Session::start() ("session has already been started").
So your code must looks like that :
Zend_Session::start();
$values = $_SESSION;
Zend_Session::writeClose();
But in Zend/Session.php:418, we have :
if (self::$_sessionStarted && self::$_destroyed) {
require_once 'Zend/Session/Exception.php';
throw new Zend_Session_Exception('The session was explicitly destroyed during this request, attempting to re-start is not allowed.');
}
So, the way you do the things is unsupported by ZF (like suggested : either with two different requests or with a batch script).
I face same issue today, so I read your blog but still did research , so I find a way which I am sharing with you.
Just use php "session_start()" rather then "Zend_Session::start();"
"Zend_Session::start();" doesn't work after session close so you can go with php session_start();
If you find any good solution please update me :)

Categories