ZF2 load service config from module - php

I am still struggling in instantiating a service from a ZF2 module outside of Zend Framework (in a blank .php).
I want to achieve:
Instantiate + invoke a ZF2 service method from outside ZF by the use of the ServiceManager and possibly DI.
What I have now: (UPDATED 4/10/2013)
Following up on the comments below I have done more research,particularly:
The quick guide
http://framework.zend.com/manual/2.0/en/modules/zend.service-manager.quick-start.html
RTD (Databases and models) http:
//zf2.readthedocs.org/en/latest/user-guide/database-and-models.html
Modules presentation (Very helpful) http://www.youtube.com/watch?v=Vp7y65rnN98#t=1200
Module source on github - https: //github.com/juriansluiman/SlmMail
I've opted to trim out all the DI and ModuleManager things and try to autoload (works fine now) and instantiate (does not) a service.
1 - Autoload the requested classes using a Classmap and instantiate servicemanager in a stand-alone .PHP file
// Autoload ZF and ProductImage module via classmap
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => TRUE,
),
'Zend\Loader\ClassMapAutoloader' => array(
'/home/frequency/domains/scrftcdn/public_html/ft/shop/php/zendframework/module/ProductImage/autoload_classmap.php',
)
)
)
// Hard-coded servicemanager configuration (will come from $module->getConfig once this works)
$smc = new \Zend\ServiceManager\Config(
array(
'service_manager' => array(
'factories' => array(
'ProductImage\Model\ProductImage' => 'ProductImage\Factory\ProductImageFactory',
)
),
)
);
// Instantiate the service manager
$sm = new \Zend\ServiceManager\ServiceManager($smc);
//Load the service via the service manager
$service = $sm->get('ProductImage\Model\ProductImage'); // <throws exception
die();
2 - The exception
[error] [client 192.168.6.52] PHP Fatal error:
Uncaught exception 'Zend\\ServiceManager\\Exception\\ServiceNotFoundException' with message 'Zend\\ServiceManager\\ServiceManager::get was unable to fetch or create an instance for ProductImage\\Model\\ProductImage' in /usr/lib/zendframework/library/Zend/ServiceManager/ServiceManager.php:495
Stack trace:\n#0 /home/frequency/domains/wpfreqad/public_html/wp-content/themes/frequency/manage-product-images/functions.inc.php(48): Zend\\ServiceManager\\ServiceManager->get('ProductImage\\Mo...')
#1 /home/frequency/domains/wpfreqad/public_html/wp-content/themes/frequency/functions.inc.php(14): require_once('/home/frequency...')\n
#2 /home/frequency/domains/wpfreqad/public_html/wp-content/themes/frequency/functions.php(14): require_once('/home/frequency...')\n
#3 /home/frequency/domains/wpfreqad/public_html/wp-settings.php(293): include('/home/frequency...')\n
#4 /home/frequency/domains/wpfreqad/public_html/wp-config.php(90): require_once('/home/frequency...')\n
#5 /home/frequency/domains/wpfreqad/public_html/wp-load.php(29): require_onc in /usr/lib/zendframework/library/Zend/ServiceManager/ServiceManager.php on line 495
3 - ProductImage\autoload_classmap.php
<?php
// Generated by ZF2's ./bin/classmap_generator.php
return array(
'ProductImageTest\Service\ProductImageServiceTest' => __DIR__ . '/test/ProductImageTest/Service/ProductImageServiceTest.php',
'ProductImage\Module' => __DIR__ . '/Module.php',
'ProductImage\Factory\ProductImageFactory' => __DIR__ . '/src/ProductImage/Factory/ProductImageFactory.php',
'ProductImage\Model\ProductImage' => __DIR__ . '/src/ProductImage/Model/ProductImage.php',
);
4 - ProductImage\Module.php
class Module implements \Zend\ModuleManager\Feature\ConfigProviderInterface
{
/* Invoked by Module Manager */
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
5 - ProductImage\config\module.config.php
<?php
return array(
'service_manager' => array(
'factories' => array(
'ProductImage\Model\ProductImage' => 'ProductImage\Factory\ProductImageFactory',
),
),
);
I hope that's the right approach and not too far off the right way..

I've finally found a solution. Jurian's hints to using the actual application have put me on the right track! :)
1 - /zendframework/config/application.config.php.
Everything is default, just make sure the module is added. I commented the 'application' module as I don't see any use for it (as of now). I also had to change the path to the config files from './module' to __DIR__ . '../module' as it was looking in the wrong directory (took me a while to find that one).
<?php
return array(
// ...
'modules' => array(
'ProductImage', /* ProductImage module */
// 'Application',
),
// ...
'module_listener_options' => array(
'module_paths' => array(
__DIR__ . '/../module',
__DIR__ . '/../vendor',
),
2 - configuration
make sure the modules are configured right, and also that ZF2 Path is set up correctly. In my case, run through the quick start on RTD (http://zf2.readthedocs.org/en/latest/ref/installation.html). I had the ZF2_PATH exception and change the httpd.conf via WHM.
3 - Read more on RTD
In particular on how you can bootstrap the application: http://zf2.readthedocs.org/en/latest/modules/zend.mvc.intro.html#zend-mvc-intro
Which after very little debugging produced me the following code to access a neatly configured $sm instance.
//wherever the ZF2 application skeleton is, include the autoloader
require_once '/home/path/to/the/ZF2/application/directory/init_autoloader.php';
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Application;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
// setup autoloader
AutoloaderFactory::factory();
// get application stack configuration
$configuration = include '/home/path/to/the/ZF2/application/directory/config/application.config.php';
//var_export($configuration);
// The init() method does something very similar with the previous example.
$app = Application::init($configuration);
$sm = $app->getServiceManager();
$pi = $sm->get('ProductImage\Service\ProductImageService');
var_export($pi);
die();
I do not like the fact that the configuration needs to be specified in addition to the init_autoloader path. I avoid this implementation from being copied and pasted all over the place, I am considering integrating the $sm instantiation into the init_autoloader.php in the future so that the path of the configuration file does not have to be specified whenever a ProductImage service needs to be invoked.

Related

How to integrate a 3rd party library with zf2?

I'm working on a zf2 project which needs to be integrated with a 3rd party library. The library is not compatible with php namespaces.
I found few articles describing the integration of namespace compatible libraries. According to them if the library is compatible with namespaces, then we can integrate it by adding a map into Module.php as follows.
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
'MyLibrary' => __DIR__ .'/path/to/library',
),
),
I tried with that and then I had to set the namespace for each and every file within the library to be able to work with the library. But it's not a good practice to modify someone else's library.
So, please help me to integrate a non-namespace compatible php library with the zf2
Here's how I did it:
In your Module.php add a ClassMapAutoloader to the getAutoloaderConfig function.
public function getAutoloaderConfig() {
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
);
}
Then create a autoload_classmap.php at the same level als Module.php that looks like this:
return array(
'MyClass' => __DIR__ . '/libs/MyClass.php',
);

Making stand-alone ZF module invokable in non-ZF application via Service Layer

I have created a Zend Framework Module which manages a set of DB and filesystem operations. I have managed to get a 100% Test Coverage (with mocked databases) and I would like to test the integration of this module into a testing environment.
I have a Service class in /modules/MyModule/src/MyModule/Service/MyModuleService.php which is currently not implementing any interfaces. The documentation doesn't hint that this is something I'd need to do.
class MyModuleService
{
public function doA(){...}
public function doB(){...}
.....
}
I have not changed anything in the application folder from the default application skeleton. I have coded up Model classes which do a lot of database and filesystem manipulation. The Service class is ultimately triggering these actions.
Having done the basic configuration according to the tutorial it seems like ZF doesn't register my classes when I try to access them outside of the ZF filesystem.
//dependencies for auto loader
require_once 'Zend/Loader/AutoloaderFactory.php';
use Zend\Loader\AutoloaderFactory;
//NEW registering the autoloader (as per Tomdarkness' response)
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => TRUE,
'namespaces' => array(
'MyModule'=> '/path/to/zendframework/module/src/MyModule',
)
)
)
);
//what factories are auto-loaded?
var_export(AutoloaderFactory::getRegisteredAutoloaders());
The var_export now returns the following:
array(
'Zend\\Loader\\StandardAutoloader' => Zend\Loader\StandardAutoloader::__set_state(
array('namespaces' =>
array('Zend\\' => '/usr/lib/zendframework/library/Zend/',
'MyModule\\' => '/path/to/zendframework/module/src/MyModule/',
), 'prefixes' => array(),
'fallbackAutoloaderFlag' => FALSE,
)
)
);
In my module.php I do declare the service as follows:
/* Configure DB Service Managers */
public function getServiceConfig()
{
return array(
// ...
'invokables' => array(
'myModuleService'=> function ($sm){
$myModuleTable = $sm->get('MyModuleTable\Model\MyModuleTable');
return new MyModuleService($myModuleTable);
}
),
}
How can I now instantiate a MyModuleService from this autoloaded class?
You call:
var_dump(AutoloaderFactory::getRegisteredAutoloaders());
But you've not actually registered any autoloaders. To register the standard autoloader you need to run:
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true
)
));
You can also register the Classmap autoloader via the factory method if you have generated classmaps.
Note, if you are using composer then you'll want to actually just include vendor/autoload.php in your file, rather than using the AutoloaderFactory. However, remember you need your module namespaces to be registered in the autoload section of composer.json if you use this method.

Include PHP Library in Zend

I have stored a third party php library in /vendor/library folder. Now i need to import it to my Zend app and use it inside controller action.
require_once ('/vendor/library/client.php');
Is this correct ? Or there is other way to to handle this ?
Use the ZF autoloader, then forget about include/require.
http://framework.zend.com/manual/1.12/en/zend.loader.autoloader.html
It means though that your class names and file names have to follow their naming conventions - which may be more trouble than it is worth.
But if you are developing your own library to work within ZF, then it is a good idea.
Adding a Composer ready 3rd party library to a ZF2 instance
The correct way to add a 3rd party library is to use Composer.
E. g. if you wish to add ZfcUser to your Zend Framework 2 application use the following command:
composer require zf-commons/zfc-user:dev-master
This will download the code from github and you just need to add the module name to your: /config/application.config.php.
Adding other 3rd party library to a ZF2 instance
If your 3rd party library is not Composer ready, you can add it to your Zend Framework 2 instance by creating a Module for it.
Step 1
/vendor/MyModule/Module.php
<?php
namespace MyModule;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
class Module implements AutoloaderProviderInterface
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
What this basically provides is a way for you to register your 3rd party code within a file called autoload_classmap.php:
Step 2
/vendor/MyModule/autoload_classmap.php
<?php
return array(
'MyModule\VendorLibrary' => __DIR__ . '/src/MyModule/VendorLibrary.php',
);
Step 3
Your 3rd party code should reside in:
/vendor/MyModule/src/MyModule/VendorLibrary.php and could read something like this:
<?php
namespace MyModule;
class VendorLibrary
{
public function sayHi($name)
{
return "Hi there, $name!";
}
// your 3rd party code ...
}
Step 4
Add your new module to application.config.php:
/config/application.config.php
<?php
return array(
'modules' => array(
// your other modules here ...
'MyModule'
),
'module_listener_options' => array(
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'./module',
'./vendor',
),
),
);
Usage
In your Controller you now use your vendor class like:
$vendor = new \MyModule\VendorLibrary();
$hi = $vendor->sayHi('John');
While it is a lot easier to use require_once(), it is not advisable because:
it does not provide predictability and structure of your class hierarchy and location
you also need to take care of include paths and make sure require_once is present in all controllers that need the 3rd party features
it does not allow for overriding classes (Magento-style)
etc.
Hope this helps!

class loading error after adding application/services to zend framework site

I'm trying to add a new folder to the application folder in zend framework, but none of the classes I create in the new folder can be found.
What I have is this structure:
application/
models/
modules/
services/
Test.php
I tried to call on the class Test.php:
class Service_Test{
}
$test = new Service_Test()
This results in the error:
PHP Warning: include_once(): Failed opening 'Service/Test.php' for inclusion (include_path='...') in library/Zend/Loader.php on line 146.
(The include_path contains a list of directories, which I removed for privacy).
I thought that Services would be automatically found in the same way that Models are automatically found. Does anyone know how I can make Zend framework find the Services folder?
I tried this as well:
require_once('Zend/Loader.php');
Zend_Loader::registerAutoload();
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => ROOT_PATH . '/application',
'namespace' => 'Service',
));
PHP Fatal error: Class 'Service_Test' not found
In your bootstrap
protected function _initNamespace(){
Zend_Loader_Autoloader::getInstance()->registerNamespace('Service_');
}
Then you call call any class that starts with 'Service_'
Thanks everyone for your help. I finally figured out what was going on. The site I am working on has an atypical setup. It's not extending any Zend Framework bootstrap class, or calling Zend_Application. The way classes were being autoloaded is by using:
set_include_path(ROOT_PATH . '/application/models'.PATH_SEPARATOR .
ROOT_PATH . '/library'.PATH_SEPARATOR .
get_include_path());
require_once('Zend/Loader.php');
#Zend_Loader::registerAutoload();
I changed it to:
set_include_path(ROOT_PATH . '/application/models'.PATH_SEPARATOR .
ROOT_PATH . '/library'.PATH_SEPARATOR .
get_include_path());
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);
$resources = new Zend_Loader_Autoloader_Resource(array(
'namespace' => '',
'basePath' => ROOT_PATH . '/application/',
));
$resources->addResourceTypes(array(
'service' => array(
'path' => 'services',
'namespace' => 'Service',
)));
Technically, I did not have to add the resource, but if I didn't, and just relied on putting /application/services into the include path, then I would not be able to prefix my classes with 'Service_'.
I'm not sure I would recommend this setup (no Bootstrap class extension or use of Zend_Application) for anyone else. It would be interesting to see what other people thought of this practice. Good or bad? In any case, that is how I solved the issue.

What directory is used for Zend plugins?

Let's say I have the following in my ini file:
resources.frontController.plugins.auth = AuthPlugin
Where should the AuthPlugin class be placed? Let's say I would like it under controllers/plugins.
UPDATE:
Based on the suggestions below I am still having trouble. Let me be exact in what I currently have:
1) main part of application.ini
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
resources.view[] =
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.plugins.authplugin.class = "AuthPlugin"
2) my Bootstrap.php has nothing (I had lots of things in there, but still get the error with nothing):
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
}
3) I have an AuthPlugin.php class in application/plugins directory
class AuthPlugin extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
// code here
}
}
I get the following error:
Fatal error: Class 'AuthPlugin' not found in C:\[my dir structure here]\Application\Resource\Frontcontroller.php on line 111
I assume I'm missing something obvious here. Thanks in advance. Zend Framework 1.10
This is how I register a plugin named Foo_Plugin_SuperDuperPlugin in my application config:
resources.frontController.plugins.superduperplugin.class = "Foo_Plugin_SuperDuperPlugin"
The plugin is located at
APPLICATION_PATH/plugins/Foo_Plugin_SuperDuperPlugin.php and is autoloaded from there because the Resource Module Autoloader automatically looks in that (recommended) location for plugin type resources. If I wanted to load the plugin from, say,
APPLICATION_PATH/controllers/plugins/Foo_Plugin_SuperDuperPlugin.php then I would register a new resource loader with the autoloader and define a type of resource named 'plugin' and the path to those plugin resources. So in my bootstrap.php
protected function _initAutoloader()
{
$autoloader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'namespace' => 'Foo',
'resourceTypes' => array(
'plugin' => array(
'path' => 'controllers/plugins',
'namespace' => 'Plugin',
)
)
)
);
}
and then I need to ensure that this method is bootstrapped before the SuperDuperPlugin is registered (which, in this example, happens when the application config is read resources.frontcontroller.plugins.superduperplugin.class = ...). This can be achieved by placing the _initAutoloader method at the top of the bootstrap.php or by calling $this->bootstrap('autoLoader'); from any other _init method, before the frontController resource is initialised.
UPDATED: Try adding this to your bootstrap:
protected function _initAutoloader()
{
$autoloader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'resourceTypes' => array(
'plugin' => array(
'path' => 'controllers/plugins',
'namespace' => '',
)
)
)
);
}
and maybe even leave off the namespace. Or: add appnamespace = "Foo" to your config and rename the class to Foo_Plugin_AuthPlugin.
hmm am having the exact same issue in zf 1.11. It seems the autoloader does not exists before the plugins get loaded :s
I think in /application/plugins/
But you could also set another Directory for it.
Since the application will bootstrap itself from the config file after registering the Autoloader, you should put AuthPlugin.php (which should contain the AuthPlugin class) in the include path.
I realise this is an old question, but I've just been searching around for ages for a solution to autoloading plugins outside of the library and have finally figured it out. Generally, I have been writing generic plugins to use across many different projects and keeping them in the library made sense, providing you follow the standard ZF naming conventions these will autoload anyway. Recently, I was writing a project specific plugin and I wanted to keep it somewhere within my application directory, but how to autoload? I've never found that using the "application/plugins" directory worked. Hopefully this might benefit someone else:
If you are using a modular directory structure then rather than using the "application/plugins" directory you can use "application/module_name/plugins". You can then take advantage of the Module Resource Autoloader, which is a massively useful and underused part of ZF. If you set it up in your bootstrap, by default it is set up to autoload a whole bunch of stuff including forms, models and (I discovered today) plugins. You can also define your own custom resource types. For example below is an _initAutoloader function from a bootstrap in a project which has a default and admin module and a custom 'vo' resource type contained in the admin module:
public function _initAutoLoader() {
$autoloader = Zend_Loader_Autoloader::getInstance();
$defaultLoader = new Zend_Application_Module_Autoloader(
array(
'namespace' => '',
'basePath' => APPLICATION_PATH . '/modules/default'
)
);
$adminLoader = new Zend_Application_Module_Autoloader(
array(
'namespace' => 'Admin',
'basePath' => APPLICATION_PATH . '/modules/admin',
)
);
$adminLoader->addResourceTypes(
array(
'vo' => array(
'path' => 'models/vo',
'namespace' => 'Vo',
)
)
);
$autoloader->pushAutoloader($defaultLoader);
$autoloader->pushAutoloader($adminLoader);
}
Here I have two modules, default and admin. Assuming the following directory structure:
application/default/forms/FooForm.php
application/default/models/FooModel.php
application/default/plugins/FooPlugin.php
application/admin/forms/FooForm.php
application/admin/models/FooModel.php
application/admin/models/vo/FooVo.php
application/admin/plugins/FooPlugin.php
I can autoload across any module just by instantiating an object of each class:
new Form_FooForm();
new Model_FooModel();
new Plugin_FooPlugin(); // or add plugin to application.ini
new Admin_Form_FooForm();
new Admin_Model_FooModel();
new Admin_Vo_FooVo(); // custom resource type
new Admin_Plugin_FooPlugin(); // or add plugin to application.ini

Categories