I'm working on a Zend Framework (1.11) application that I am porting into modules so that the devs can plug and play various additions to our software and remove just as easily.
The issue I'm having (and don't seem to be able to find an answer for is) I have dependable library code, i.e my structure is currently this
- application
- (the standard ZF stuff)
- modules
- testModule
- Bootstrap.php
- controllers
- configs
- views
- library
- CoreStuff
- Class.php
- SpecialClass.php
- testModuleLibrary
- Class.php
- SpecialClass.php
And what I really want is this so that it is easier for the other devs to install modules to our system.
- application
- (the standard ZF stuff)
- modules
- testModule
- Bootstrap.php
- controllers
- configs
- views
- library
- Class.php
- SpecialClass.php
- library
- CoreStuff
- Class.php
- SpecialClass.php
Can I use the Autoloader in the module Bootstrap? or do I have to add it to my include path?
[EDIT]
This is my current module bootstrap, I've seen this code time and time again with my perilous Googling but it doesn't appear to make any difference
<?php
class Notifications_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initLibraryAutoloader () {
return $this->getResourceLoader()->addResourceType('library', 'library', 'library');
}
}
I have resolved my issue with the following code. I literally have no idea why I didn't come up with this earlier :( :(
class Notifications_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initConfig () {
set_include_path(implode(PATH_SEPARATOR, array(
dirname(__FILE__) . '/library',
get_include_path(),
)));
}
}
You can define your library paths in the application config file like:
This is example in YML
project:
bootstrap:
path: APPLICATION_PATH/Bootstrap/index.php
class: Bootstrap_Index
Autoloadernamespaces:
- Zend_
- Library1_
- Library2_
resources:
FrontController:
moduledirectory:
- APPLICATION_PATH/modules
......
Here is example in INI format
[bootstrap]
Autoloadernamespaces[] = "Zend_"
Autoloadernamespaces[] = "Library1_"
Autoloadernamespaces[] = "Library2_"
resources.FrontController.moduleDirectory = APPLICATION_PATH"/modules"
resources.FrontController.resetRouterOnEveryRequest = true
bootstrap.path = APPLICATION_PATH "/Bootstrap/index.php"
bootstrap.class = "Bootstrap_Index"
Regarding the project directory structure I advise you to use something similar to:
- application
- Bootstrap
- index.php
- Modules
- Mod1
- controllers
- views
- Mod2
- controllers
- views
...
- library
- Zend
- ...
- Library1
- ...
- Library2
- ...
Having done the directory structure you can have URLs like /:module/:controller/:action and keep all 3rd party code separate in its own pool Library directory
You may just use the modules' Boostrap.php.
Provide a function
protected function _initAutoload(){}
to make the lib available. Thus you may just make the module-developer do it, as it is part of the work developing a module to make it's resources loadable :)
I'm adding this response to hopefully both answer the original inquiry, as well as provide some clarification on a few items submitted here. I'm an active developer of modules in ZF 1.11, and use what I'm about to explain everyday in one of the several modules we maintain.
My apologies ahead of time for the length of this response. There are several items to cover and consider.
First, for the implementation.
The following snippet that was provided as far as I know worked ~1.8, and would not be what you want for 1.11.
<?php
class Notifications_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initLibraryAutoloader () {
return $this->getResourceLoader()->addResourceType('library', 'library', 'library');
}
}
The following though would work fine provided a few key elements I'll explain below.
protected function _initLibraryAutoloader () {
return $this->getResourceLoader()->addResourceType('library', 'library', 'Library_');
}
You'll notice a slight difference in the third parameter ( the namespace ). The best explanation of this would be to possibly update the function to the following:
protected function _initLibraryAutoloader () {
$this->getResourceLoader()->addResourceType('library', 'library', 'Library_');
var_dump($this->getResourceLoader()->getResourceTypes());die;
}
Your output should be something similar to:
Array
(
[dbtable] => Array
(
[namespace] => Admin_Model_DbTable
[path] => /path/to/trunk/application/modules/admin/models/DbTable
)
[mappers] => Array
(
[namespace] => Admin_Model_Mapper
[path] => /path/to/trunk/application/modules/admin/models/mappers
)
[form] => Array
(
[namespace] => Admin_Form
[path] => /path/to/trunk/application/modules/admin/forms
)
[model] => Array
(
[namespace] => Admin_Model
[path] => /path/to/trunk/application/modules/admin/models
)
[plugin] => Array
(
[namespace] => Admin_Plugin
[path] => /path/to/trunk/application/modules/admin/plugins
)
[service] => Array
(
[namespace] => Admin_Service
[path] => /path/to/trunk/application/modules/admin/services
)
[viewhelper] => Array
(
[namespace] => Admin_View_Helper
[path] => /path/to/trunk/application/modules/admin/views/helpers
)
[viewfilter] => Array
(
[namespace] => Admin_View_Filter
[path] => /path/to/trunk/application/modules/admin/views/filters
)
[library] => Array
(
[namespace] => Admin_Library
[path] => /path/to/trunk/application/modules/admin/library
)
)
If you compare this to your previous "library" as the third parameter, you'll see in a second why it makes such an important difference.
To reiterate, at this point, you've declared a library type with the prefix of "Library_", which is translating to "Admin_Library". Now, to implement this, you'll have your library folder in your module just as you do in your main application module, with one slight adjustment. To have an Admin module specific controller action ( Admin_Library_Controller_Action ), you'll have library/Controller/Action.php, with a class name of Admin_Library_Controller_Action. This is what most find confusing at first, but it's very similar to the other resource namespaces you should be using in modules.
That concludes the technical explanation. If you read no further, you'll be able to have a library specific to your module and totally self-contained for easy copy/paste implementation of a reusable module.
Now for a few comments about some of the other responses I saw here.
infinity noted
"If you follow your structure you might end up with - Module1 (Lib1, Lib2) - Module2 (Lib1, Lib3) so you'll start duplicating the needed libraries."
This is technically correct, but really brings to light more issues in your actual development style if you find yourself routinely using the same libraries all over the place. We generally store these common items in another library, that we use as an external, identical to Zend, and is placed along side Zend in the main application library folder.
Provided you're already doing that, Where I believe some additional clarification might be needed is, if you're using the namespaces correctly, you absolutely don't have to worry about a collision of library class names. You may for a short period of time find that you need to have classes with identical code in multiple modules, but as soon as this happens, you should consider the common library in addition to Zend that I mentioned above.
My apologies again for the length of this response. I hope that it helps anyone who might come across this post in need of library implementation in modules.
Related
This question is related to an earlier question I had, but I learned a bit more now, and that question became too messy, so I start a new question here.
I am trying to use Yii and PHPUnit at the same time, and try to get the Autoloading working. I found an Autoloader here that I am using for PHPUnit. I read here that I should register the Yii autoloader last.
With the following bootstrap file:
<?php
require_once(__DIR__.'/../tests/AutoLoader.php');
Toolbox\Testing\AutoLoader::registerDirectory(__DIR__.'/../protected');
require_once(__DIR__.'/../yii-1.1.14.f0fee9/yii.php');
print_r(spl_autoload_functions());
?>
I see that the autoloaders are in place in the right order:
[1] => Array
(
[0] => Toolbox\Testing\AutoLoader
[1] => loadClass
)
[2] => Array
(
[0] => YiiBase
[1] => autoload
)
However, I still can't import the class I need. It goes wrong in YiiBase in the function autoload in the line include($className.'.php');, which tries to include a class name without a path attached to it.
Why does Yii try to load a class without a full path to the file needed?
How can I make this combination to work?
We are building a web application using Yii as the framework. Where would be a good location for us to put a version information array?
This version array is not the version of Yii but the version our application is on. This way we can use it global throughout the application. Example when deploy the application on our servers we can have a conditional that compares the required_php_version against the server's php version (phpversion()) to throw errors. This is just a simple example.
The array would consist of (with possibility to evolve later):
<?php
array(
'version' => '2.0.1',
'required_php_version' => '5.4.4'
);
?>
As far as I know, The best place to put your configurations in an application based on Yii, is main.php config file, which is situated in protected/config/main.php. But it is important to put your custom configurations in a right place. That is in params array. You can put your configs like below in config file:
'params' => array(
'webmaster' => 'YourEmail#example.com',
'required_php_version' => '5.4.1',
'my_app_version'=>'2.0.1.1',
'info_in_array'=>array(1,2,3,4,'so on ...')
// and so on
),
You can use these information in everywhere of your application like below:
Yii::app()->params['required_php_version'] //which returns 5.4.1 in this example.
It should be "trivial", but after some chating on #laravel irc channel, I found it may be impossible for now. But I'll ask it here before doing it the ugly-go-horse way just to have the project done. If it's indeed impossible by current means, I'll fill a request on github (after handing over the project to my client).
I'm using Zizaco\Confide to handle authentication in my service. It uses Laravel Lang everywhere to get strings in one of the 8 bundled languages of the package. But I need to override some of those strings, and I don't want to modify the package files (which would defeat the whole purpose of Composer). How can I do this?
For example, I needed to modify confide::confide.alerts.wrong_credentials for pt_BR language. What I tried so far:
/app/lang/pt_BR/confide.php file, with contents return array('alerts' => array('wrong_credentials' => '...')). It works for Lang::get('confide.alerts.wrong_credentials') but not for the namespaced Lang::get('confide::confide.alerts.wrong_credentials')
/app/lang/pt_BR/packages/zizaco/confide/confide.php with return array('alerts' => ......)
/app/lang/pt_BR/packages/zizaco/confide/confide/alerts.php with return array('wrong_credentials' => ...)
/app/lang/packages/zizaco/confide/pt_BR/confide.php with array('alerts' => array('wrong_credentials' => '...')) - /app/lang/packages/zizaco/confide/pt_BR/confide/alerts.php with return array('wrong_credentials' => ...)
Any clue on what am I missing? Or does Laravel4 really lacks this feature?
Thanks in advance!
actually it fixed in Laravel 4.1 core
you can now overwrite it by doing
app/lang/packages/(locale)/confide/confide.php
check this
laravel 4 language issue
correct path for overriding package languages
So, as for today, Laravel really does lack this feature.
I've asked for it creating a issue on github.
Meanwhile, this functionality could be achieved seamlessly using crynobone's Orchestra Platform 2 Translation Component, which can be found here
All you need to do is require it in composer.json
{
"require": {
"orchestra/translation": "2.0.*"
}
}
and replace the original translation package ('Illuminate\Translation\TranslationServiceProvider') in /config/app.php
'providers' => array(
//'Illuminate\Translation\TranslationServiceProvider',
// ...
'Orchestra\Translation\TranslationServiceProvider',
),
That's it! Now, having app/lang/en/packages/confide/confide.php will do it!
(please note that the path should be /packages/packagename/, not /packages/vendor/packagename/
It really saved me from a big headache, hope others find this useful too.
I have a new module for which I'm writing tests.
The module contains a class which implements ServiceLocatorAwareInterface because it needs to create other objects using the DI container. Everything works fine when running in the skeleton app, but when running module tests i get the following:
Zend\Di\Exception\RuntimeException: Invalid instantiator of type "NULL" for "Zend\ServiceManager\ServiceLocatorInterface"
Researching a little bit I find that the DI container tries to create a new object of type "ServiceLocatorAwareInterface", which is of course wrong.
Digging a little more in the tests bootstrap, I find that adding the following line solves the problem, as in the DI now knows what class to instantiate for that interface.
$di->instanceManager()->addTypePreference('Zend\ServiceManager\ServiceLocatorInterface', new \Zend\ServiceManager\ServiceManager());
I'm not sure whether this is the best solution to the problem, as the ServiceManager passed by me is a dummy one.
Does anyone have any other ideas?
Yes, you are going in the right direction. (See the preferences documentation)
Not many people are using DI these days in favor of the ServiceManager (myself included), but if the config for DI remains similar to how it was during the ZF2 betas, you should be able to add a "preferences" section to your DI config like so:
'di' => array(
'instance' => array(
'preferences' => array(
'My_Interface' => 'My_Implementation_Or_Alias',
)
)
)
This configuration block can replace your call to $di->instanceManager()->addTypePreference()
Looking through the current docs, and mimicking the example here, you may have success defining the DI config as shown below using the ZF2 official release:
$di = new Zend\Di\Di;
$di->configure(new Zend\Di\Config(array(
'instance' => array(
'preferences' => array(
'My_Interface' => 'My_Implementation_Or_Alias',
)
)
)));
What you can do in this case is the following.
In your bootstrap for the module unit tests create a dummy application that is configured with a configuration that will only load the module you're testing.
...//other code before this for autoloading stuff
// DON'T RUN THE application in your tests, just init it
$application = Zend\Mvc\Application::init(include 'config/test.application.config.for.module.php');
$fullyConfigedManager = $application->getServiceManager();
TestCases::setServiceManager( $fullyConfigedManager );
After the application has been boostrapped you can pull the ServiceManager from the application directly. This service manager should be fully configured with any factories, invokables, and configuration from your module.
Hi there people and greetings from Sweden!
I have a really tricky problem here. I'll try to show exactly what I mean.
I'm building a modulebased CMS, and some modules inherit from a parent module. My problem is that the parent modules need to be included before the "children".
I fetch moduleinfo from a XML file and store it in an array like this:
Array
(
[bloggy] => Array
(
[module_id] => blog
[module_name] => Blog
[module_desc] => Description
[module_url] => http://www.url.se
[author] => Dev Name
[author_url] => http://url.se
[version] => 1.0
[inherit] => core|twitter
[path] => /path/to/file
[dependon] => Array
(
[0] => core
[1] => twitter
)
)
I've done a explode on inherit and saved it into "dependon" as you see above. The problem now is, how can I sort which order to include the files. Every module inherits from core but if there is another module in the depenon array then the "child" module must be included after.
I hope you understand what I mean?
// Tobias
Look up "topological sort".
You could build you modules as classes and then use the __autoload magic function to automatically include/require all needed php files.
That way it's much less of a headache when you have complex dependencies.
Refer to the PHP manual for details on autoloading.
Just include all dependencies in your files. Try
// module1.php
require_once 'core.php'
// module2.php
require_once 'core.php'
require_once 'module1.php'
// module3.php
require_once 'core.php'
require_once 'module1.php'
require_once 'module2.php'
Including module3 will also include module2, module1 and core. You could leave out the core and module1 in module 3 and it would still load them all, but then you have to know what includes which.
Or use autoloading and don't bother about it.
Hmm I'll try to explain a little better.
I search the module folder for the xml info file and save the data into an array, I also save the path to the file in the array.
Then I use a foreach loop to include and instantiate the modules. My problem is that the parent modules must be instantiated before the "children".
I do not want to touch the core files when adding a new module, I need to use the hooks on a parent module.