How Does the Zend Application / Bootstrapping Work? - php

A few questions regarding the basics of Zend Framework 1.9.
i followed the quickstart guide, and basically, bootstrapping involves,
a. from index.php:
$ZEND_FRAMEWORK_LIB_PATH = '/appl/ZendFramework-1.9.7/library';
defined('APPLICATION_PATH') || define('APPLICATION_PATH', (realpath(dirname(__FILE__) . '/../application')));
defined('APPLICATION_ENV') || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
set_include_path(implode(PATH_SEPARATOR, array((dirname(dirname(__FILE__)) . '/library'), $ZEND_FRAMEWORK_LIB_PATH, get_include_path(),)));
require_once 'Zend/Application.php';
$application = new Zend_Application(APPLICATION_ENV, (APPLICATION_PATH . '/configs/application.ini'));
$application->bootstrap()->run();
b. Then in the Bootstrap.php, i have
protected function _initAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array("namespace" => "Default_", "basePath" => dirname(__FILE__),));
return $autoloader;
}
protected function _initDoctype()
{
$this->bootstrap("view");
$view = $this->getResource("view");
$view->doctype("XHTML1_STRICT");
}
For a start, a few things i don't understand:
a. If a user access the site not via the default index.php, does that mean that bootstrapping (and indeed, all the code in the index.php including setting of environment etc, will be bypassed?)
b. There is no place that explicitly calls the Bootstrap's _initAutoload() or _initDoctype() methods. So when are these methods implicitly invoked?
c. Since in the index.php, i have already "passed in" the config file '/configs/application.ini' to the Zend_Application constructor, is there any way to retrieve the config entries elsewhere?
In my application, i have to work with different databases (so i can't just use resources.db.*). So in the same application.ini file, i have, e.g.
custdb.adapter = "PDO_MYSQL"
custdb.params.host = "localhost"
custdb.params.username = "username"
custdb.params.password = "password"
custdb.params.dbname = "custdb"
What's the best practice to manage the DB adapter?
a. Is it possible to (and should i) create the DB adapter in index.php OR Bootstrap.php and retrieve it elsewhere when needed (and how)?
b. Or is possible to (and should i) just retrieve the config entries elsewhere (how?) and instantiate the DB adapter as and when needed?
Thanks!

Here's a few answers.
2a. All request are redirected to index.php. This is done with mod_rewrite and specified in the .htaccess file.
2b. The bootstrap calls any method prefixed with _init. See Zend Framework - Theory of Operation
2c. Yes. Zend::Config. You could store an instance in Zend::Registry for easy access. Eg:
$config = new Zend_Config((APPLICATION_PATH . '/configs/application.ini'));
$application = new Zend_Application(APPLICATION_ENV, $config);
Zend_Registry::set('config', $config);
Check the API reference to see the constructors for these two classes.
I don't think the Quick start is that helpful. I'd recommend a getting a book. I enjoyed "Zend Framework 1.8 Web Application Development" by Keith Pope.

To answer question 3, ZF uses the Application Resource Plugin Zend_Application_Resource_Db to ingest the config and create a database adapter instance.
If your need for multiple databases is an environmental thing, you can easily namespace your DB params in your application.ini file.
[production]
resources.db.adapter = PDO_MYSQL
resources.db.params.host = localhost
resources.db.params.username = user
resources.db.params.password = pass
resources.db.params.dbname = production_db
[staging : production]
resources.db.params.dbname = staging_db
[development : production]
resources.db.params.dbname = development_db
In this example, we're setting up common info in the [production] section and overriding it for our staging and development environments. Which config is applied is controlled by the environment variable in your app's .htaccess
If you need to access multiple databases in a single application, then I would recommend rolling your own Application Resource Plugin, and creating some kind of structure to hold multiple connections.
It's not as difficult as it might seem. Read up on it here and create a subclass of of Zend_Application_Resource_ResourceAbstract. Using this class, you can easily grab resources.* in your config file using:
$this->getBootstrap()-getResource('mydb')`
You would then have access to your plugin via the bootstrap object:
$bootstrap->getPluginResource('mydb')
Hope that helps.
EDIT: I forgot to mention, if you have a resource plugin as part of your application.ini, the Zend_Application bootstrapper will automatically know to include it as part of the bootstrap proccess, so you don't need to define any _init() method in your bootstrap file. It's kind of magic like that.
Also, as far as storing an adapter instance, I'd probably just user Zend_Registry.

thanks for all your replies! It really helped my understanding of the concept of ZF.
i've also gone through the references and source codes to get a deeper understanding, and this is what i adopted:
In my application.ini i have:
custom.db.customers.adapter = "PDO_MYSQL"
custom.db.customers.params.host = "localhost"
custom.db.customers.params.username = "username"
custom.db.customers.params.password = "password"
custom.db.customers.params.dbname = "custdb"
Then, in my Bootstrap.php i have:
protected function _initCustomDbCustomers()
{
$config = $this->getOptions();
$cfgCustom = $config['custom'];
if (null != $cfgCustom)
{
$cfgCustomDb = $cfgCustom['db'];
if (null != $cfgCustomDb)
{
$cfgCustomDbCustomers = $cfgCustom['customers'];
if (null != $cfgCustomDbCustomers)
{
$resrcCustomDbCustomers = new Zend_Application_Resource_Db($cfgCustomDbCustomers);
return $resrcCustomDbCustomers
}
}
}
}
Of course, in my index.php, i call:
$application->bootstrap();
$application->run();
Then, in the controller where i need to get the DB adapter, i do:
$bootstrap = $this->getInvokeArg('bootstrap');
$resrcCustomDbCustomers = $bootstrap->getResource('customDbCustomers');
$adpCustomDbCustomers = $resrcCustomDbCustomers->getDbAdapter();
// Do Stuffs With DB Adapter
Is this a Good/Bad way to do things? And is there any pitfall i should watch for?
Thanks!

Related

defining constant values & accessing globally in zend framework 2

I am new to this zend2, previously I worked with CAKEPHP & codeigniter. I want to write some constant values in a particular file & be able to access them any where in the project.
In cakephp it is like Configure::write('environment','dev'); we write this in a file in Config folder which will be at app/Config/file name
and we can access this like $env = Configure::read('environment'); any where..
Can we do in the same way in zend framework 2, like defining the constants in a file & can access them anywhere..?
Please give an example how to define & read it with the path of the file
No is the short answer. Cake, ZF1, CodeIgniter all made use of a design pattern, now widely discouraged, called the the Registry Pattern (which is really just a Singleton).
The very fact that this class is globally accessible, is one of the reasons why its use is not advised.
ZF2 has a completely different architecture and offers a flexible approach by merging configuration based on environment variables. An when it comes to 'using' the configuration, you should be injecting it into your services using the service manager and a service factory.
First define variable in config
Like:-
accountUser.admin = "admin"
Then initialize object like(in controller or model):-
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/config.ini', APPLICATION_ENV);
// APPLICATION_PATH is path to project
// APPLICATION_ENV will be your environment
Then use it like:-
$admin = $config->accountUser->admin;

Zend Framework project without using Zend_Application

I've been reading on many sites even here that in order to improve performance of Zend Framework applications is not to use the Zend_Application in bootstrap, but haven't been able to find a site that has this demonstrated.
Do you guys know of a place that has this method described and might provide me with some code samples?
Thanks
I just threw this together:
https://gist.github.com/2822456
Reproduced below for completion. Not tested, just some ideas for how I think it generally (!) might work. Now that i have walked through it a bit, I have a greater appreciation for Zend_Application, its bootstrap classes, and its configurable/reusable application resources. ;-)
// Do your PHP settings like timezone, error reporting
// ..
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/_zf/application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
// Get autoloading in place
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
// Any additional configs to autoloader, like custom autoloaders
// Read config
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', APPLICATION_ENV);
// bootstrap resources manually:
// * create db adapter
// * create resource autoloaders with the mappings you need
// * etc
// Get the singleton front controller
$front = Zend_Controller_Front::getInstance();
// Set controller directory
$front->setControllerDirectory(APPLICATION_PATH . '/controllers');
// Or set module directory
$front->setModuleDirectory(APPLICATION_PATH . '/modules');
// Other front config, like throw exceptions, etc.
// ...
//
// Create a router
$router = new Zend_Controller_Router_Rewrite();
// Add routes to the router
$router->addRoute('myRoute', new Zend_Controller_Router_Route(array(
// your routing params
)));
// More routes...
// Alternatively, the routes can all be in an xml or ini file and you can add
// them all at once.
// Tell front to use our configured router
$front->setRouter($router);
// Add an plugins to your $front
$front->registerPlugin(new My_Plugin());
// other plugins...
// Dispatch the request
$front->dispatch();
There might be some View/ViewRenderer stuff to do, as well. But as noted in other places, the ViewRenderer incurs a non-trivial performance hit. If performance is the issue, then you'll want to disable the ViewRenderer and make your action controllers call their own rendering using $this->view->render('my/view-script.phtml')
When you call $front->dispatch(), the $request and $response objects will be created automatically. If you want to do something specific to them at bootstrap - like setting the charset in Content-Type header of the response - then you can create your request/response object yourself, do what you want to it, and then attach it to the front with $front->setResponse($response); Same for the request object.
Although I see that my example uses Zend_Loader_Autoloader and Zend_config_Ini which Padraic notes incur performance hits. The next step would be to address those by using arrays for config, stripping require_once calls from the framework, registering a different autoloader, etc, an exercise left for the reader... ;-)
Hi I disagree somewhat with the not using Zend_Application in bootstrap and no I have yet to see a concrete example of this technique.
Personally I don't see the benefit in not using Zend_app for bootstrapping your application, assuming a) Your doing things 'the Zend way' and b) your project is either big enough or just simply warrants using the Zend framework (or any for that matter).
While Zend_App is great for creating consistent complex bootstraps
within a standardised structure, it doesn’t come without a significant
performance hit to baseline performance. A more direct bootstrap
(typical of ZF until Zend_App arrived) is far faster and can also be
done without configuration files.
Taken from Pádraic Brady link.
Well to me the above does not make sense, he basically just said Zend_App is great for complex bootstraps, but adds a performance hit. But isn't that the premise for ANY framework / framework component? I know Padraic is a very clever guy and I'm sure he has his reasoning, but I too would love to see examples / evidence of this suggestion.
Perhaps in answer to your question, you could benchmark a basic app using the latest Zend framework and then use Zend framework from < 1.10 using the old non Zend_App way, but I would say although clearly not perfect Zend_App is clearly faster to get most app's up and running, so whether this is worth 'the performance hit' is I guess up to the developer(s).
Here is a link which somewhat goes into what you may be after but makes reference to a modular approach (still interesting, none the less):
http://www.osebboy.com/blog/zend-framework-modules/

Zend framework Krixton_JsonRpc_Client and call method

I'm trying to figure out what is going on below:
public function serviceAction(){
$config = Zend_Registry::get('config');
$client = new Zend_Http_Client();
$client->setAuth($config['api']['username'],$config['api']['password']);
$service = new Krixton_JsonRpc_Client($config['api']['endpoint'], $client);
switch($this->_getParam('task'))
{
case 'test':
if(!this->getParam('newsletter_id')){
$this->_helper->json(array('sent'=>false,'error'=>'Newsletter ID is invalid, must be numeric'));
return;
}
$request = $service->call('newsletter.send', array($this->_getParam('newsletter_id'),false));
$this->_helper->json($request->result));
break;
}
}
What I'm trying to find out is how does
`Zend_Registry::get('config')`, $client->setAuth and $service->call`
works?
I understand _getParam('task') is a method to get get or post variables but not sure about the others. I had a look through some Zend documentations but if someone could help me out that would be appreciated!
Two things are happening there, the first one is Zend_Registry. get()allow you to get a value previously registered in the registry via Zend_Registry::set('key', $value). Usually, 'config' is your application configuration, which is the application.ini file.
basically, you would register config using this bootstrap method:
protected function _initConfig()
{
$config = new Zend_Config($this->getOptions());
Zend_Registry::set('config', $config);
return $config;
}
The second ones are nothing else than methods of Zend_Http_Client. setAuth()is used to set a basic HTTP authentification, and call()is an internal method of your object Krixton_JsonRpc_Client.
If you're trying to understand deep down how does these methods work, you should read the man first (especially Zend_registry and Zend_Http_Client pages) and then read carefully the source code.
Zend_Registry::get('config') ('config' is name of an array in this case) is recalling data that was saved to the registry, probably in the Bootstrap.php to make the information in the application.ini(configuration file) available everywhere.
The Bootstrap.php probably caontains something similar to:
protected function _initRegistry() {
//make application.ini configuration available in registry
$config = new Zend_Config($this->getOptions());
Zend_Registry::set('config', $config);
}
$client->setAuth is simply providing user credentials to Zend_Http_Client() HTTP LINK that were stored in the configuration file and accessed through the $config array.
$service->call I'm quite sure what this doing because I'm not familiar with the class (likely custom) being used. It looks like a request for a newsletter is being made based on 'id'.

zend model - no adapter found error

have defined different models and modelMappers in my php project.
my folders are structured as follows
-application
--models
---DbTable
----modelName
---modelMapperName
modelName.php
class Application_Model_DbTable_ModelName extends Zend_Db_Table_Abstract{
}
modelMapper.php
class Application_Model_ModelMapper
{
}
everytime i try to access the values in my database by creating an instance of my mapper in the controller i get the message No adapter found for Application_Model_DbTable_ModelName
i thought this can be an issue with the autoloader and added the following code in the bootstrap,
$loader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH
));
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);
return $loader;
but it still didnt work, can anyone help me figure out where i am going wrong?
thank you.
This error it's related to database initialization...
You have to configure the database adapter in your application.ini:
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "user"
resources.db.params.password = "password"
resources.db.params.dbname = "db"
Or you could also configure the database adapter in your bootstrap file:
$db = Zend_Db::factory("pdo_mysql", $params);
Zend_Db_Table::setDefaultAdapter($db);
Since the variable APPLICATION_PATH returns de full path to your application folder try naming your classes only
Model_DbTable_ModelName
i found out that the variable i was trying to access didn't start with an underscore and also capitalization issues with the models created was causing the problem.
Thank you all for helping

How to setup a Zend_Application with an application.ini and a user.ini

I am using Zend_Application and it does not feel right that I am mixing in my application.ini both application and user configuration.
What I mean with this is the following. For example, my application needs some library classes in the namespace MyApp_ . So in application.ini I put autoloaderNamespaces[] = "MyApp_". This is pure application configuration, no-one except a programmer would change these. On the other hand I put there a database configuration, something that a SysAdmin would change.
My idea is that I would split options between an application.ini and an user.ini, where the options in user.ini take preference (so I can define standard values in application.ini).
Is this a good idea? How can I best implement this? The idea's I have are
Extending Zend_Application to take multiple config files
Making an init function in my Bootstrap loading the user.ini
Parsing the config files in my index.php and pass these to Zend_Application (sounds ugly)
What shall I do? I would like to have the 'cleanest' solution, which is prepared for the future (newer ZF versions, and other developers working on the same app)
I found a solution to this issue that may be new to framework version 1.10. When creating the Zend Application object, you can pass in 2 configuration file paths in the options array that get merged together:
$application = new Zend_Application(
APPLICATION_ENV,
array(
'config' => array(
APPLICATION_PATH . '/configs/application.ini',
APPLICATION_PATH . '/configs/user.ini'
),
)
);
you know this will merge as much inis as you want?
in application.ini
[production]
config[] = APPLICATION_PATH "/configs/dsn.ini"
config[] = APPLICATION_PATH "/configs/error.ini"
...
There is nothing wrong with that, I do something similar. I suggest using your second choice. I just have an _initConfig() method that takes care of loading the user config using Zend_Config_Ini. I wouldn't extend Zend_App, that seems a bit much.
Edit:
In response to your comment, you would simply do:
$this->bootstrap('config');
Thus, to ensure config is loaded before DB, you'd have something like:
protected function _initConfig()
{
$config = new Zend_Config_Ini('/path/to/user.ini');
return $config;
}
protected function _initDb()
{
$this->bootstrap('config');
$config = $this->getResource('Config');
/* ... */
}
There is no need to use Zend_Registry as anything returned by a Bootstrap _init method is accessible using getResource()
An configuration file can have the item 'config' which refers to another config file. Zend_Application will include this config file. The included config-file will have preference, and overwrite the keys already defined in the standard config-file.
Yesterday there was also started a thread on the Zend Framework mailing list
Examples
application.ini:
[production]
config = APPLICATION_PATH "/configs/config.ini"
resources.db.adapter = "Mysqli"
resources.db.host = "localhost"
config.ini:
[production]
resources.db.host = "mysql.server.com"
resources.db.username = "myuser"
public/index.php:
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
In a similar scenario, I saw that the application specific parameters can be provided programmatically when instantiating the application. This helped to place configuration related parameters within the config.ini
I did it actually this way:
inside the index.php to bootstap the application
$application = new Zend_Application(APPLICATION_ENV, array(
'resources' => array(
'FrontController' => array(
'controllerDirectory' => APPLICATION_PATH . '/main/controllers',
),
'layout' => array(
'layoutpath' => APPLICATION_PATH . "/layouts/scripts"
),
),
));
and then inside the bootstrap parse the config.ini inidependently
protected function _initConfigFile() {
try {
$configuration = new Zend_Config_Ini(
APPLICATION_PATH . '/config/app.ini',
APPLICATION_ENV );
$registry->configuration = $configuration;
} catch (Zend_Exception $zExp) {
echo "Could not read application ini file (app.ini). "
. " Please check that it exists and has the appropriate structure.\n";
echo("\n");
var_dump($zExp);
exit(1);
}
}
inside the bootstrap
You can ensure an _initConfig() bootstrap method is invoked before others by specifying in your other bootstrap methods (that require the config object) something like:
$this->bootstrap('config');
A more complete example (context of a Bootstrap class):
protected function _initConfig() {
$config = new Zend_Config_Ini('[filename]');
Zend_Registry::set('config',$config);
}
protected function _initSomething() {
$this->bootstrap('config');
$config = Zend_Registry::get('config');
// you can now do whatever you like with the $config object
}
Update:
As has now been mentioned in other answers, if the config is only required within the bootstrap, I would say to use the $this->getResource('Config') method. I use the registry so that config can be accessed easily in other parts of my application.

Categories