CI 2.2.x upgrade to CI 3.0.x core files issues - mysqli

I just followed the upgrade guide for CI2 to CI3 and don't have encountered any other issues so far. Will probably get some because of the project I converted still uses DataMapper and has some other hooks and third party tools.
So the first problem I encountered so far is that my custom core controllers don't work anymore.
I use for example this depth for my front-end and back-end logic:
CI_Controller > MY_Controller > CronController
CI_Controller > MY_Controller > FrontendController
CI_Controller > MY_Controller > AdminController
CI3 tells me that my controllers/Account.php controller doesn't know the FrontendController class.
Can I tell CI3 to still include my custom classes or is there a new approach for this?

If you used __autoload(){$class} for autoloading your core classes, you should change it with spl_autoload_register(function ($class){}. I.e.:
spl_autoload_register(function ($class) {
if (substr($class,0,3) !== 'CI_') {
if (file_exists($file = APPPATH . 'core/' . $class . EXT)) {
include $file;
}
}
});

Related

Smarty seems to ignore addTemplateDir

I'm working in a Zend Framework based application (Shopware).
I add a template dir in my controller like this:
class Shopware_Controllers_Backend_Pricify extends Shopware_Controllers_Backend_ExtJs
{
public function init()
{
$this->View()->addTemplateDir(dirname(__FILE__) . "/../../Views/backend/");
parent::init();
}
}
But somehow, smarty always looks in the (not existing) part of the controller action:
Unable to load template snippet 'backend/mycontroller/model/main.js' in 'snippet:string:{include file="backend/pricify/model/main.js"} in Smarty/sysplugins/smarty_internal_templatebase.php on line 128
The Controller works over loading via ext js, but I do not see that this is a problem. When I var_dump template directories, the correct dir is included. I debugged the code far into smarty, but never found the part, where the directories are checked.
I'm aware, that this may be a problem within the software stack, but since I do not know where to search, I ask here. If I need to post additional data, please tell me.
I found, that the problem was that shopware extends CamelCase to camel_case folders.

Accessing the DI container from anywhere

I've implemented the Symfony2 Dependency Injection container in my Zend Framework project and it works fine in the MVC layer of my application. I've initialized the DIC in my bootstrap and can access it anywhere by calling:
Zend_Controller_Front::getInstance()->getParam('bootstrap')->getDic()
The problem is that there are some parts of my application that do not utilize the Zend Framework application/MVC layer. My CLI tools for example. I could perfectly initialize a new DIC there but that would just be some copy paste work from the Bootstrap file which is asking for trouble down the road (DRY principles, etc)
Is it a better solution to make my DIC available in the Zend_Registry or as a singleton called by a static method DIC::getInstance() for example?
I know Registry and singletons are considered bad things but the DIC is such a high level part of the application that I will probably never run into the problems that make it a bad thing.
Is this a good solution or are there better ways of accomplishing a globally accessible DIC?
I achieved this in the past using Pimple (created by Fabien Potencier, owner of Symfony).
Pimple is a small Dependency Injection Container for PHP 5.3 that consists of just one file and one class (about 50 lines of code).
Here is how I coupled it with my ZF1 application:
Create a new Pimple container into your application's bootstrap
Declare all your services with proper dependencies
Access the DIC through your controllers or CLI tools
Access the services through the DIC
If your services are well declared (injecting dependencies through their constructors) you shouldn't have to access the DIC outside your controllers or CLI tools.
Use a base controller class to easily access the DIC through $this->container:
abstract class MyApp_Controller_Action extends Zend_Controller_Action
{
protected $container;
public function init()
{
$this->container = Zend_Controller_Front::getInstance()
->getParam('bootstrap')->getDic();
}
}
In order to use your DIC into your CLI tools:
Extend Zend_Application to create your CLI application
Override run() to prevent the MVC stack to bootstrap
When creating your CLI tool inject the DIC through its constructor
Use a base command class to easily access the DIC through $this->container:
abstract class MyApp_Command
{
protected $container;
public function __construct($container)
{
$this->container = $container;
}
}
To have access to your bootstrap ressources in your CLI file you can go and do a partial bootstrapping of your application
Instead of doing this (public/index.php) and bootstraping your whole application :
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
->run();
You can do this and only bootstrap the required resouces :
$app = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
// Selectively bootstrap resources:
$app->bootstrap('db');
$app->bootstrap('log');
$app->bootstrap('autoload');
$app->bootstrap('config');
$app->bootstrap('di');
You have to make sure that you initialize them in the right order (you might need to have your DB loaded before the logging component if you have a DB writter for your logs for example).
From there, you can call parts of your bootstrap (for the DI component, you can call the $app->getBootstrap()->getContainer(). You have access to all methods available in your bootstrap.

PHPExcel class not found in Zend Autoloader

I am struggling with namespaces in Zend Framework (at least I think it's a namespace issue).
I want to integrate PHPExcel into my Zend project. Relevant file structure is as follows:
/
-library
-ABCD
-PHPExcel
-Zend
-ZendX
-PHPExcel.php
Custom classes work fine, after
Zend_Loader_Autoloader::getInstance()->registerNamespace('ABCD_');
in the bootstrap. Also, those classes are all named ABCD_blahdeblah.
However, doing registerNamespace('PHPExcel_') doesn't help Zend find the appropriate classes. When I try
$sheet = new PHPExcel;
in the controller, I get a "Class not found" error. I am guessing that this is either because classes in PHPExcel aren't named with the namespace prefix, or because the main PHPExcel.php file sits outside of the namespace I've just declared. But the PHPExcel structure demands that it sit in the parent directory of the rest of the class/font/etc files.
Any pointers would be greatly appreciated.
Thanks in advance.
Create an autoloader for PHPExcel and add it to the Zend autoloader stack.
In library/My/Loader/Autoloader/PHPExcel.php:
class My_Loader_Autoloader_PHPExcel implements Zend_Loader_Autoloader_Interface
{
public function autoload($class)
{
if ('PHPExcel' != $class){
return false;
}
require_once 'PHPExcel.php';
return $class;
}
}
And in application/configs/application.ini:
autoloadernamespaces[] = "My_"
Then, in application/Bootstrap.php:
protected function _initAutoloading()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader(new My_Loader_Autoloader_PHPExcel());
}
Then you should be able to instantiate PHPExcel - say, in a controller - with a simple:
$excel = new PHPExcel();
The only sticky part is all of this is how PHPExcel handles loading all its dependencies within its own folder. If that is done intelligently - either with calls like require_once basename(__FILE__) . '/someFile.php' or with its own autoloader that somehow doesn't get in the way of the Zend autoloader - then all should be cool. #famouslastwords
Nowadays composer is a frequently used tool that wasn't so popular back in 2012. Even older projects built in ZF1 can make use of composer and its autoloader.
How to get all your libraries to work without having to add custom autoloaders to your application.ini each time?
Make use of composer's autoloader
First, start with setting up composer.json. Once created, run composer install to gather all required packages and create composer's autoloader.
Now, let's update your project's public/index.php. From now on all requirements that are loaded via composer will be autoloaded.
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'development'));
// Include composer autoloader
require_once __DIR__ . '/../vendor/autoload.php';
/** Zend_Application */
require_once 'Zend/Application.php';
// Create application, bootstrap, and run
$application = new Zend_Application(
APPLICATION_ENV,
array( 'config' => APPLICATION_PATH . '/configs/application.ini' )
);
$application->bootstrap();
$application->run();
Try modifying the PHPExcel autoloader:
Add
if (function_exists('__autoload')) {
spl_autoload_register('__autoload');
}
as the first two lines of the Register() method in /Classes/PHPExcel/Autoloader.php, immediately before
return spl_autoload_register(array('PHPExcel_Autoloader', 'Load'));
I've had a similar problem with both an exel-librare (phpxls) and a pdf-library (fpdf) and after some different tries I just settled with including the required file from the library manually and go from there. Booth phpxls and fdpd can then handle everything else without interfering with the zend autoloader methods.
A psudo_code example would look like this, where I return a object of the desired class and then can continue to work with that. You could offcourse choose to include things in the constructor and build from that.
<?php
class exelClass{
public function exelFunction(){
require_once 'required_file.php';
$exelObject = new exelObject();
return $exelObject->Output();
}
}
?>
This solution might not be that elegant, but I found that it was the easiest way to enable different types of libraries to co-exist without differnet autoloaders or magic functions interfearing with each other.

Can't override the standard skeleton views in Symfony2 GeneratorBundle

I don't manage to override the skeleton views of the generatorBundle.
I've first tried by adding my view in /app/Resources/SensioGeneratorBundle/skeleton/crud/views/index.html.twig
It didn't worked so I tried to create a new Bundle extending SensioGeneratorBundle and copy the my view in its Resources folder.
I already manage to use themes for twig forms, but I need to personalize the views generated by the doctrine:generate:crud command.
First of all: The corresponding skeleton views are located here:
vendor/bundles/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud
Quick and dirty you should be fine by overriding these view files - but thats not what we want ;)
In:
vendor/bundles/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.php
there is an accessor for the Generator:
protected function getGenerator()
{
if (null === $this->generator) {
$this->generator = new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/crud');
}
return $this->generator;
}
One can try to override this method in your extending Bundle and set a different $skeletonDir in the constructor.
Edit:
Quick example in my test environment how it can be achieved (I only made a quick test ;):
Generate a new bundle for the custom generator: php app/console generate:bundle and follow the instructions. A route is not needed. I chose for this example: Acme/CrudGeneratorBundle (Or use an existing bundle)
Create a folder called "Command" in the newly created bundle directory.
Place a command class in this folder.
<?php
//src/Acme/CrudGeneratorBundle/Command/MyDoctrineCrudCommand.php
namespace Acme\CrudGeneratorBundle\Command;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator;
class MyDoctrineCrudCommand extends \Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand
{
protected function configure()
{
parent::configure();
$this->setName('mydoctrine:generate:crud');
}
protected function getGenerator()
{
$generator = new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/crud');
$this->setGenerator($generator);
return parent::getGenerator();
}
}
Copy the vendor/bundles/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud to your Resources (in my example "src/Acme/CrudGeneratorBundle/Resources/crud")
This was the best solution for me:
symfony2-how-to-override-core-template
doesn't add a command but modifies the skeleton for that particular bundle.

How to load plugins with modules

I have this plugin in the plugins directory within the admin module directory. So, it is in application/modules/admin/plugins/LayoutPlugin.php:
<?php
class LayoutPlugin extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
$view->whatever = 'foo';
}
}
I'd like to use it for sending variables to the layout view. It happens that I get Fatal error: Class 'LayoutPlugin' not found every time I try Zend_Controller_Front::getInstance()->registerPlugin(new LayoutPlugin()); in the admin bootstrap.
How do I load a plugin inside a module?
Module bootstraps setup the module autoloader by default, so if you rename your class to Admin_Plugin_LayoutPlugin ZF should be able to find it.
Keep in mind that the admin bootstrap (like all bootstraps) will be run regardless of whether you're in the admin module or not, so if your intention is to assign some extra variables just for your admin pages you'll need to ensure that admin is the current module before registering the plugin.
I know this is an old question but this is always something that bugged me. I don't know if ZF 2 has done anything to solve it (I've not had the chance to play with it yet), but I wrote a plugin loader plugin to deal with this for ZF 1!
The problem is, of course, that even when setting up a module autoloader and keeping your plugins in a module's plugin folder, this only setups up autoloading (which is cross module anyway) not registration. It means you can instantiate the plugin with a line in your application.ini, but it will be autoloaded and registered for every module.
Anyway, here's a possible solution to make sure module plugins are only registered for the active module. Alternatively, instead of providing a class map, you could loop through all the files in a module's plugins directory, but that feels ugly... and probably slow.
<?php
class BaseTen_Controller_Plugin_ModulePluginLoader extends Zend_Controller_Plugin_Abstract {
private $_pluginMap;
public function __construct(array $pluginMap) {
$this->_pluginMap = $pluginMap;
}
public function routeShutdown(Zend_Controller_Request_Abstract $request) {
$module = $request->getModuleName();
if(isset($this->_pluginMap[$module])) {
$front = Zend_Controller_Front::getInstance();
foreach($this->_pluginMap[$module] as $plugin) {
$front->registerPlugin(new $plugin());
}
}
}
}
Because we need to pass a classMap to the constructor, we need to explicity instantiate and register this plugin with the Front Controller rather than with a line in application.ini:
public function _initPluginLoader() {
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new BaseTen_Controller_Plugin_ModulePluginLoader(array(
'default' => array(
'Plugin_Foo',
'Plugin_Bar',
...
),
'foo' => array(
'Foo_Plugin_Foo',
'Foo_Plugin_Bar',
...
)
)));
}
The earliest the plugin can run is at routeShutdown otherwise we won't know the active module. What this means though is that any other plugins registered using this method can only run from dispatchLoopStartup onwards. Mostly, we're probably interested in preDispatch and postDispatch hooks but worth bearing in mind.