I need to create a service (for jwt logic) that will be standalone, but I see that it is not so easy to refer to the service I have to use 'new' and unfortunately then I create a new instance... :/ but I want to work globally (using public methods) on one and store the token value.
application/service/Jwt.php
<?php
class Application_Service_Jwt
{
private $token;
public function getToken()
{
return $this->token;
}
public function setToken($token)
{
$this->token = $token;
}
}
application/configs/services.global.php
<?php
return [
"services" => array(
'Jwt' => new Application_Service_Jwt()
)
];
application/Bootstrap.php
protected function _initServiceManager()
{
$conf = glob(__DIR__ . "/configs/services.global.php", GLOB_BRACE);
$serviceManagerConfigurator = new \Laminas\ServiceManager\Config($conf["services"]);
$serviceManager = new \Laminas\ServiceManager\ServiceManager();
$serviceManagerConfigurator->configureServiceManager($serviceManager);
// Register it into zend registry is not mandatory
\Zend_Registry::set("serviceManager", $serviceManager);
return $serviceManager;
}
This is how to implement Laminas Service Manager in ZF1 application. This also work with ZF2 and ZF3 Service manager since they are the same.
In your composer add this dependency
"laminas/laminas-servicemanager": "^3.4"
In your Bootstrap.php add this _init function
protected function _initServiceManager()
{
$files = glob(__DIR__ . "/configs/{config,services}.{global,".APPLICATION_ENV."}.php", GLOB_BRACE);
$conf = [];
foreach ($files as $file) {
$conf = array_replace_recursive($conf, include($file));
}
$serviceManagerConfigurator = new \Laminas\ServiceManager\Config($conf["services"]);
$serviceManager = new \Laminas\ServiceManager\ServiceManager();
$serviceManagerConfigurator->configureServiceManager($serviceManager);
// Register it into zend registry is not mandatory
\Zend_Registry::set("serviceManager", $serviceManager);
return $serviceManager;
}
Now, under application/configs you should create the services.global.php file, where you can add your services definition (read the doc for all the possibilities it offers, ZF2 version is ok )
return [
"services" => [,
"factories" => [
"hello" => function() {
return "OK";
}
]
]
In the same folder, add config.{ENVIRONMENT}.php files, where you can define properties depending on your ENVIRONMENT ( they are also loaded depending on its value )
return [
"myApiConfig" => [
"endpoint" => "...",
"username" => "...",
"password" => "...",
],
From your controller you can access your service manager instances using both getResource or Zend registry, you can add this in your overridden init
$this->serviceManager = $this->getFrontController()->getParam('bootstrap')->getResource('serviceManager');
And get any defined service
$this->serviceManager->get('hello');
You can also separate your service classes that use proper php namespaces adding the autoload key in your composer ( change the namespace name and directory as you wish )
"autoload": {
"psr-4": {
"MyNewLibs\\": "library/"
}
},
Related
I made new plugin with informations find at this post: https://luketowers.ca/blog/how-to-use-laravel-packages-in-october-cms-plugins/
I update composer.php and in vendor folder i got created files I see plugin phpclasses/evalmath in backend.
When on page i try do math operation:
function onStart() {
// instantiate a new EvalMath
$m = new EvalMath;
$m->suppress_errors = true;
// set the value of x
$m->evaluate('x = 3');
var_dump($m->evaluate('y = (x > 5)'));
}
I got error that Class 'EvalMath' not found Class is defined in file /plugins/phpclasses/evalmath/vendor/phpclasses/evalmath/evalmath.class.php What i am doing wrong?
in file /plugins/phpclasses/evalmath/composer.json
{
"require": {
"phpclasses/evalmath": ">=1.0.0"
},
"repositories": [
{
"type": "composer",
"url": "https:\/\/www.phpclasses.org\/"
},
{
"packagist": false
}
]
}
in file /plugins/phpclasses/evalmath/Plugin.php
<?php namespace phpclasses\evalmath;
use App;
use Config;
use System\Classes\PluginBase;
use Illuminate\Foundation\AliasLoader;
/**
*
* Class Plugin */
class Plugin extends PluginBase
{
/**
*
* Returns information about this plugin.
* #return array
*/
public function pluginDetails()
{
return ['name' => 'phpclasses/evalmath',
'description' => 'OctoberCMS plugin for demonstrating the use of Laravel Packages within October plugins',
'author' => 'hhh',
'icon' => 'icon-leaf'
];
}
/**
*
* Runs right before the request route */
public function boot()
{
// Setup required packages $this->bootPackages(); }
/**
*
* Boots (configures and registers) any packages found within this plugin's packages.load configuration value
* #see https://luketowers.ca/blog/how-to-use-laravel-packages-in-october-plugins
* #author Luke Towers octobercms#luketowers.ca
*/
public
function bootPackages()
{ // Get the namespace of the current plugin to use in accessing the Config of the plugin $pluginNamespace = str_replace('\', '.', strtolower(NAMESPACE));
// Instantiate the AliasLoader for any aliases that will be loaded
$aliasLoader = AliasLoader::getInstance();
// Get the packages to boot
$packages = Config::get($pluginNamespace . '::packages');
// Boot each package
foreach ($packages as $name => $options) {
// Setup the configuration for the package, pulling from this plugin's config
if (!empty($options['config']) && !empty($options['config_namespace'])) {
Config::set($options['config_namespace'], $options['config']);
}
// Register any Service Providers for the package
if (!empty($options['providers'])) {
foreach ($options['providers'] as $provider) {
App::register($provider);
}
}
// Register any Aliases for the package
if (!empty($options['aliases'])) {
foreach ($options['aliases'] as $alias => $path) {
$aliasLoader->alias($alias, $path);
}
}
}
}
}
}
in file /plugins/phpclasses/evalmath/classes/config.php
<?php
return [
// This contains the Laravel Packages that you want this plugin to utilize listed under their package identifiers
'packages' => [
'phpclasses/evalmath' => [
],
],
];
the most of the code in file /plugins/phpclasses/evalmath/Plugin.php(bootPackages()) is not nessesary if you dont have configs or additional providers or aliases
if its a laravel package you can use \App::register('\Your\LaravelPackage\ServiceProvider'); in the boot function
to register the package with the laravel Provider
and ad an alias for your package
$alias = \Illuminate\Foundation\AliasLoader::getInstance()->alias('YourAlias', '\Your\LaravelPackage\Facade');
if its not a laravel package try use the full namespace i think its \EvalMath if you use this package https://www.phpclasses.org/browse/file/11680.html
We are using Doctrine 2 in our app, but due to our infrastructure, we do not have a static configuration for database connections. Instead, we have a collection of singletons in a service provider for each database we need to connect to, and we select a random database host for then when we connect.
Unfortunately, we are seeing some performance degradation in Doctrine's getRepository() function. I believe the issue is that Doctrine needs to generate its proxy classes at runtime (even in production) because we cannot figure out how to configure the CLI tools in order to create them at build time.
We are using the Laravel framework for the application.
Here's an example of our Laravel service provider which makes the repositories available for dependency injection.
<?php
use App\Database\Doctrine\Manager as DoctrineManager;
use Proprietary\ConnectionFactory;
use App\Database\Entities;
use App\Database\Repositories;
use App\Database\Constants\EntityConstants;
class DoctrineServiceProvider extends ServiceProvider
{
// Create a singleton for the Doctrine Manager. This class will handle entity manager generation.
$this->app->singleton(DoctrineManager::class, function ($app)
{
return new DoctrineManager(
$app->make(ConnectionFactory::class),
[
EntityConstants::ENTITY_CLASS_DATABASE1 => [app_path('Database/Entities/Database1')],
EntityConstants::ENTITY_CLASS_DATABASE2 => [app_path('Database/Entities/Database2')],
],
config('app.debug'),
$this->app->make(LoggerInterface::class)
);
});
// Register the first repository
$this->app->singleton(Repositories\Database1\RepositoryA1::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE1)
->getRepository(Entities\Database1\RepositoryA1::class);
});
// Register the second repository
$this->app->singleton(Repositories\Database1\RepositoryA2::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE1)
->getRepository(Entities\Database1\RepositoryA2::class);
});
// Register a repository for the second database
$this->app->singleton(Repositories\Database2\RepositoryB1::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE2)
->getRepository(Entities\Database2\RepositoryB1::class);
});
}
Here's the class that generates EntityManagers for Doctrine:
<?php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Connections\MasterSlaveConnection;
use Proprietary\ConnectionFactory;
class Manager
{
private $c_factory;
private $paths;
private $connections = [];
private $entity_managers = [];
public function __construct(
ConnectionFactory $cf,
array $paths
)
{
$this->c_factory = $cf;
$this->paths = $paths;
}
public function getConnection($name, $partition = false, $region = false)
{
// Get a list of servers for this database and format them for use with Doctrine
$servers = self::formatServers($name, $this->c_factory->getServers($name, true, $partition, $region));
// Generate a connection for the entity manager using the servers we have.
$connection = DriverManager::getConnection(
array_merge([
'wrapperClass' => MasterSlaveConnection::class,
'driver' => 'pdo_mysql',
], $servers)
);
return $connection;
}
public function getEntityManager($name, $partition = false, $region = false)
{
// Should these things be cached somehow at build time?
$config = Setup::createAnnotationMetadataConfiguration($this->paths[$name], false);
$config->setAutoGenerateProxyClasses(true);
// Set up the connection
$connection = $this->getConnection($name, $partition, $region);
$entity_manager = EntityManager::create($connection, $config);
return $entity_manager;
}
// Converts servers from a format provided by our proprietary code to a format Doctrine can use.
private static function formatServers($db_name, array $servers)
{
$doctrine_servers = [
'slaves' => [],
];
foreach ($servers as $server)
{
// Format for Doctrine
$server = [
'user' => $server['username'],
'password' => $server['password'],
'host' => $server['hostname'],
'dbname' => $db_name,
'charset' => 'utf8',
];
// Masters can also be used as slaves.
$doctrine_servers['slaves'][] = $server;
// Servers are ordered by which is closest, and Doctrine only allows a
// single master, so if we already set one, don't overwrite it.
if ($server['is_master'] && !isset($doctrine_servers['master']))
{
$doctrine_servers['master'] = $server;
}
}
return $doctrine_servers;
}
}
Our service classes use dependency injection to get the repository singletons defined in the service provider. When we use the singletons for the first time, Doctrine will use the entity class defined in the service provider and get the connection associated with the repository.
Is there any way we can enable the CLI tools with this configuration? Are there any other ways that we can optimize this for use in production?
Thanks.
I was able to solve the problem thanks to a suggestion from the Doctrine IRC channel. Since the CLI tools can only handle a single database, I created a doctrine-cli directory containing a base-config.php file and a subdirectory for each of the databases we use.
Here's an example file structure:
doctrine-cli/
|- database1/
| |- cli-config.php
|- database2/
| |- cli-config.php
|- base-config.php
The base-config.php file looks like this:
<?php
use Symfony\Component\Console\Helper\HelperSet;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use App\Database\Doctrine\Manager as DoctrineManager;
use Proprietary\ConnectionFactory;
require __DIR__ . '/../bootstrap/autoload.php';
class DoctrineCLIBaseConfig
{
private $helper_set;
public function __construct($entity_constant, $entity_namespace)
{
$app = require_once __DIR__ . '/../bootstrap/app.php';
// Proprietary factory for getting our databse details
$connection_factory = new ConnectionFactory(...);
// Our class that parses the results from above and handles our Doctrine connection.
$manager = new DoctrineManager(
$connection_factory,
[$entity_constant => [app_path('Database/Entities/' . $entity_namespace)]],
false,
null,
null
);
$em = $manager->getEntityManager($entity_constant);
$this->helper_set = new HelperSet([
'db' => new ConnectionHelper($em->getConnection()),
'em' => new EntityManagerHelper($em),
]);
}
public function getHelperSet()
{
return $this->helper_set;
}
}
Here's an example cli-config.php from the database directory:
<?php
use App\Database\Constants\EntityConstants;
require __DIR__ . "/../base-config.php";
$config = new DoctrineCLIBaseConfig(
EntityConstants::ENTITY_CLASS_DATABASE1,
"database1"
);
return $config->getHelperSet();
Now, I'm able to cycle through each of the directories and run commands like so:
php ../../vendor/bin/doctrine orm:generate-proxies
For our build process, I wrote a simple shell script that cycles through the directories and runs the orm:generate-proxies command.
I'm trying to set up a zend framework 3 MVC web app to use session storage. Following the information from this website --
https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/Working_with_Sessions/PHP_Sessions.html
It all works well. I get the session variable in my controller and I can save data to the session container just fine. The problem is, the data I save to the container is NOT there on subsequent calls. I'm saving search criteria from one page and doing a redirect to a second page to do the search and return the results. The session data is not present when I enter the second page.
In config\global.php I have --
return [
'session_config' => [
// Cookie expires in 1 hour
'cookie_lifetime' => 60*60*1,
// Stored on server for 30 days
'gc_maxlifetime' => 60*60*24*30,
],
'session_manager' => [
'validators' => [
RemoteAddr::class,
HttpUserAgent::class,
],
],
'session_storage' => [
'type' => SessionArrayStorage::class,
],
];
In application\module.php i have modified onBoostrap
public function onBootstrap(MvcEvent $event)
{
$application = $event->getApplication();
$svcMgr = $application->getServiceManager();
// Instantiate the session manager and
// make it the default one
//
$sessionManager = $svcMgr->get(SessionManager::class);
}
I created an IndexControllerFactory
class IndexControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null)
{
// Get access to session data
//
$sessionContainer = $container->get('Books\Session');
return new IndexController($sessionContainer);
}
}
Modified my IndexController to add a constructor method
class IndexController extends AbstractActionController
{
private $session;
public function __construct(Container $session)
{
$this->session = $session;
}
In application\module.config.php i have this
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\Factory\IndexControllerFactory::class,
],
],
'session_containers' => [
'Books\Session'
],
To store something in session you can create the container as follows:
// Create a session container
$container = new Container('Books\Session');
$container->key = $value;
To retrieve something from a session container later you have to create a new container with the same name:
// Retrieve from session container
$container = new Container('Books\Session');
$value = $container->key;
As far as I know this works similarly for both ZF2 and ZF3 and can be found in other posts on StackOverflow or for example this blog post with the title Using Sessions in Zend Framework 2 .
If you create a new Container for storing or resolving data from the session it will automatically use the default session manager if you don't pass one yourself.
You can see that here in the AbstractContainer::__construct method on line 77. If the $manager passed to the constructor is null it will get the default session manager inside the setManager method.
So to use sessions you don't need to do a lot of manual configuration.
If that doesn't solve your problem please leave a comment.
I need to optionally include some modules in my ZF2 app. The modules are entirely independent with any loaded modules.
In application.config.php, in the config array I can just include the main modules, and then, at the end, based on some conditions, to add the optional module. Like this:
$config = array(
'modules' => array(
'Application',
),
...
);
if (condition) {
$config['modules'][] = 'OptionalModule';
}
return $config;
Though this works and fixes the problem, I was wondering if there is another way of doing this.
Is it a good approach for this use case? Would there be a nicer way to accomplish this?
Thanks!
I usually do this by using any of the below two methods:
Conditionally load module with local application config
application.config.php:
<?php
use Zend\Stdlib\ArrayUtils;
$config = [
// You config
];
$local = __DIR__ . '/application.config.local.php';
if (is_readable($local)) {
$config = ArrayUtils::merge($config, require($local));
}
return $config;
application.config.local.php:
<?php
return [
// Your config
];
This enables you to have a base application config and load an additional config per deployment. So no if $condition, this is determined by your deployment process, which is most times easier to manage.
Note this also works for deployment configs: application.config.development.php vs application.config.production.php. This is just whatever you like, to suit your needs.
Conditionally execute code in module config
In your Module.php
<?php
namespace MyModule;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$sm = $app->getServiceManager();
$config = $sm->get('Config');
if ($config['mymodule']['enabled'] === true) {
// condition
}
}
}
Then you can have your module.config.php in your own module folder:
<?php
return [
'mymodule' => [
'enabled' => true,
],
];
But if youn need to disable this in a certain environment, you add this to your config/autoload/local.php:
<?php
return [
'mymodule' => [
'enabled' => false,
],
];
We're using ZendFramework at my workplace for our webapps. It's ok, but it lacks some of the best modern practices (like dependency injection and inversion of control, aop, etc).
For a couple of months, I've been (on my own) using Ding framework as a container for DI and AOP as a test drive. I really like it, so I'd like to bring it into our projects.
But how? So there's the question: how to properly integrate Ding in Zend Framework applications? considering ZF controllers cant be beans (as they are instantiated right from the dispatcher), how to propertly inject all dependencies in them?
P.s: Not using Zend Framework is not an option (at least in the middle term).
P.P.S: Anyone care to add "ding" as a new tag?
I'm glad Ding is helping you.
I contributed on this project and also needed to integrate with a Zend Framework application. I used Zend's application resources and plugin system to achieve this.
An application resource (you can reuse among projects)
<?php
class Application_Resource_Ding extends Zend_Application_Resource_ResourceAbstract
{
protected $_options = array(
'factory' => array(
'bdef' => array(
'xml' => array(
'filename' => array('beans.xml')
),
),
),
'cache' => array(
'proxy' => array('impl' => 'dummy'),
'bdef' => array('impl' => 'dummy'),
'beans' => array('impl' => 'dummy')
)
);
public function init()
{
// set default config dir before mergin options (cant be set statically)
$this->_options['factory']['bdef']['xml']['directories'] = array(APPLICATION_PATH .'/configs');
$options = $this->getOptions();
// parse factory properties (if set)
if (isset($options['factory']['properties'])) {
$options['factory']['properties'] = parse_ini_file(
$options['factory']['properties']
);
}
// change log4php_properties for log4php.properties (if exists)
if (isset($options['log4php_properties'])) {
$options['log4php.properties'] = $options['log4php_properties'];
unset($options['log4php_properties']);
}
$properties = array(
'ding' => $options
);
return Ding\Container\Impl\ContainerImpl::getInstance($properties);
}
}
An action helper to use inside the controllers:
<?php
class Application_Action_Helper_Ding extends Zend_Controller_Action_Helper_Abstract
{
protected $ding = null;
public function init()
{
// just once...
if (null !== $this->ding) {
return;
}
// get ding bootstrapped resource
$bootstrap = $this->getActionController()->getInvokeArg('bootstrap');
$ding = $bootstrap->getResource('ding');
if (!$ding) {
throw new Zend_Controller_Action_Exception(
'Ding resource not bootstrapped'
);
}
$this->ding = $ding;
}
public function getBean($bean)
{
return $this->ding->getBean($bean);
}
public function direct($bean)
{
return $this->getBean($bean);
}
}
In your application.ini you should add something like this (plus any extra configuration you need)
resources.frontController.actionHelperPaths.Application_Action_Helper = "Application/Action/Helper"
resources.ding.factory.properties = APPLICATION_PATH "/configs/ding.properties"
resources.ding.log4php_properties = APPLICATION_PATH "/configs/log4php.properties"
And then in your controllers, to request a bean:
$service = $this->_helper->ding('someService');
Hope this helps!