I'm trying to implement custom exception classes for a zend project.
I use modules and all my classes are under library.
so a filename DuplicateFileException.php which is under
APPLICATION_PATH "/../library/Abra/Exception" contains
class Abra_Exception_FileNotFoundExcpetion extends Exception {}
class Abra_Exception_MissingFileException extends Exception {}
class Abra_Exception_DuplicateFileException extends Exception {}
class Abra_Exception_FileIOException extends Exception {}
so the ErrorController works fine when i only throw Abra_Exception_DuplicateFileException
because there is indeed a file called DuplicateFileException, but the app breaks when i throw any other than that Exception.
I just can't believe that i have to create a file for each of them.
so how to work around it?
thanks for reading.
Proper zend-way would be implementing your own autoloader class. IT will implement autoload method. You can then use Zend_Autoloader autoloader stack to oad your classes. You just need to have some kind of system/logic in your classname->filename mapping.
class Abra_Autoloader
{
public function autoload($className)
{
if (strpos('Abra_Exception', $className) !== false) {
include $someFile; //faster
}
}
}
//anywhere in bootstrap (preferably in some autoloading section of yours)
// autoloader will load only classes starting with "abra"
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);
$autoloader->pushAutoloader(array('Abra_Autoloader ', 'autoload'), 'Abra');
Please note that at least teh autoloader class should be Zend_Loader-loadable :)
The Zend autoloader works by mapping requested class names to filesystem paths so you can't make it load a file for a class where the filename does not match.
If you really want to persist down this path, I'd just include the file in your Bootstrap class, eg
protected function _initRequires()
{
require_once 'Abra/Exception/DuplicateFileException.php';
// assuming your "library" folder is on the include path
}
Related
I have a problem with Codeception/AspectMock.
When using custom autoloader and try to create an instance of a class which has parent form the same custom namespace I have this error:
PHP Fatal error: Uncaught InvalidArgumentException: Class [parent
class name] was not found by locator in
vendor/goaop/parser-reflection/src/ReflectionEngine.php:112
I have very simple setup:
<?php
require_once __DIR__ . '/vendor/autoload.php';
$kernel = AspectMock\Kernel::getInstance();
$kernel->init([
'debug' => true,
'includePaths' => [__DIR__. '/lib'],
]);
$kernel->loadFile(__DIR__ . '/autoload.php'); // custom autoloader
$b = new \lib\B();
Class \lib\B:
namespace lib;
class B extends A {}
Class \lib\A:
namespace lib;
class A
{
public function getName()
{
return static::class;
}
}
Class B is loaded via my custom autoloader, but then the locator tries to load parent class A via composer autoloader and returns this error. Is this a bug, or I'm doing something wrong?
The topic starter has already got an answer on GitHub.
In order to use custom autoloader you should re-init ReflectionEngine with composite class locator that will be able to locate your classes or you can use CallableLocator with closure for resolving paths.
Or, even better you could switch your code base to the PSR0/PSR-4
For example:
$kernel->loadFile(__DIR__ . '/autoload.php'); // custom autoloader
\Go\ParserReflection\ReflectionEngine::init(
new class implements \Go\ParserReflection\LocatorInterface {
public function locateClass($className) {
return (new ReflectionClass($className))->getFileName();
}
}
);
$b = new \lib\B(); // here you go
If you can easily do a find and replace on your codebase, maybe you could refactor your code to PSR-4 autoloading standards and do away with the need for a custom autoloader altogether.
This is the spec https://www.php-fig.org/psr/psr-4/. I'll try and explain it as simply as possible.
Imagine changing your lowercase namespace lib to Lib, and setting that namespace to the src/ directory in your composer.json:
"autoload": {
"psr-4": {
"Lib\\": "src/"
}
}
After setting that, run composer dumpautoload. Then all you need to do is search and replace namespace lib;, replacing with namespace Lib;.
An example class located in src/Form.php would have namespace Lib; at the top, followed by class Form.
<?php
namepace Lib;
class Form
{
// code
}
Namespaces use the folder naming convention. All classes directly in src/ have namespace Lib;. If there are subdirectories, the directory name becomes part of the namespace. For example a file in src/Form/Field/Text.php would have namespace Lib\Form\Field; class Text {}.
<?php
namepace Lib\Form\Field;
class Text
{
// code
}
You can see the full convention in the link above, but the general rule is make any folders begin with a capital letter, as with your classname, and the autoloader should be able to find all of your classes.
This is probably the best practice solution for you, and again as I said, only requires a little bit of file renaming and namespace tweaking. Good luck!
I'm trying to write some test cases for a ZF application I'm writing but I can't seem get past this point.
I have extended the Zend_Test_PHPUnit_ControllerTestCase, adding the following:
public function setUp() {
$this->bootstrap = new Zend_Application('testing',
APPLICATION_PATH . '/configs/application.ini');
parent::setUp();
}
It works, and it initializes the Zend Autoloader, allowing me to load other classes inside my library as well as Zend classes. So I setup a test:
public function testUserUnauthorized() {
$this->dispatch('/api/user');
//Assertions...
}
Sadly, the test never gets past the dispatch. Instead, it gives me an error:
Fatal error: Class 'App_Model_User' not found in ....Action.php
Action.php is a class extending Zend_Controller_Action. In it, there is a function that uses App_Model_User to authenticate the logged in user. I never had to add a require_once since the Autoloader works. That's the strange part: it works through my browser. Just not in PHPUnit.
So I dumped the Autoloaders registered with the Zend Autoloader:
public function testUserUnauthorized() {
print_r(Zend_Loader_Autoloader::getInstance()->getAutoloaders());
exit;
$this->dispatch('/api/user');
}
It shows all my modules, as well as the namespaces for views, controllers, models, etc. I did the same through my browser and the dumps matched.
Zend Autoloader needs App_ added as an autoload prefix for this to work as you expect, something like this:
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace("App_");
I need to create some base exception classes and inherit from it.
I see two approaches:
1. Create folder 'Exceptions' in library and use standard autoload mechanism
/library
/Exception/
Base.php
InvalidParameter.php
2. Just declare exception classes in model, that uses it.
Is there a better more "Zend-ish" approach?
If you look into zend library, what they normally do is
/library
Exception.php
/Exception/
InvalidParameter.php
And InvalidParameter Class would look something like Exception_InvalidParameter extends Exception. It is quite similar to what you have suggested in option 1.
I generally love to have all my custom library classes in to /library/custom
The purpose of creating specific exception classes is to associate it with some specific functionality (database , session etc) . If your functionality can be fully described in one class for example
Class AdminController extends Zend_Controller_Action
{
public function loginAction()
{
throw new LoginException('Failed login');
}
}
Class LoginException extends Exception {
}
Note both classes are in same file AdminController.php . This approach is faster since no autoloading is needed to load LoginException since its in same file.
I am using a custom scheme in naming my files. Any pointers on how to autoload them with Zend's autoloader?
Thanks!
You can write your own autoloader class and register it with the ZF autoloader. There is a brief bit about this in the manual: http://framework.zend.com/manual/en/zend.loader.autoloader.html#zend.loader.autoloader.interface
Basically it's just a class defining one method which takes the class name as the parameter. So for you it would be something like:
class My_Autoloader implements Zend_Loader_Autoloader_Interface
{
public function autoload($class)
{
$filename = $class.'.obj.php';
require $filename;
}
}
the above assumes the classes are on the include path, if not, just stick the full path in front of $filename. Add any other mapping/checking you need in here and then register it as per the example in the manual. Normally you would do this in your bootstrap.
I want to use the HTMLpurifier in combination with the Zend Framework. I would love to load the Class and its files with the Zend_Loader. How would you include it? Would you just use the HTMLPurifier.auto.php or do you know a better way of doing it?
I use HTML Purifier as a filter in my Zend Framework project. Here's an altered version of my class:
require_once 'HTMLPurifier.includes.php';
require_once 'HTMLPurifier.autoload.php';
class My_Filter_HtmlPurifier implements Zend_Filter_Interface
{
protected $_htmlPurifier = null;
public function __construct($options = null)
{
// set up configuration
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.DefinitionID', 'My Filter');
$config->set('HTML.DefinitionRev', 1); // increment when configuration changes
// $config->set('Cache.DefinitionImpl', null); // comment out after finalizing the config
// Doctype
$config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
// configure caching
$cachePath = APPLICATION_PATH . '/../cache/htmlpurifier';
if (!is_dir($cachePath)) {
mkdir($cachePath, 0755, true);
}
$cachePath = realpath($cachePath);
$config->set('Cache.SerializerPath', $cachePath);
if (!is_null($options)) {
//$config = HTMLPurifier_Config::createDefault();
foreach ($options as $option) {
$config->set($option[0], $option[1], $option[2]);
}
}
$this->_htmlPurifier = new HTMLPurifier($config);
}
public function filter($value)
{
return $this->_htmlPurifier->purify($value);
}
}
Unless I'm misunderstanding the question (Or HTMLpurifier). If you have Zend_Loader running and it's set to autoload.
require_once('Zend/Loader.php');
Zend_Loader::registerAutoload();
Or something to that effect. Put the HTMLpurifier class in your library directory. I'm just not sure on it's actual class name.
You can just put the class file in the library directory and call it by it's name, or maybe toss it in a misc package.
Examples
// SITE/library/Zend/Auth.php
class Zend_Auth
{
}
// SITE/library/htmlpurifier.php
class htmlpurifier
{
}
// SITE/library/misc/htmlpurifier.php
class Misc_HTMLpurifier
{
}
Make sense?
you can register an autoloader class using the Zend_Loader class. when you call the registerAutoLoad() method without any parameters, you are actually registering Zend_Loader itself as an autoloader. so:
Zend_Loader::registerAutoLoad();
// equals to: Zend_Loader::registerAutoLoad('Zend_Loader'),true);
Zend_Loader tries to load classes using Zend Framework's naming convention, which is like this:
each class is defined in a separate file
each class name begins with a capitalized letter
underlines in class name, means a directory level.
so if 'Zend_Loader' is the name of a class, it is defined in the file 'Loader.php' in 'Zend' directory in your path. to you PHP can file load this class from file Zend/Loader.php
if your classes follow this naming convention, they can be automatically loaded using the same autoloader. else, you need to define your own autoloader. write an autoloader class winch can extend Zend_Loader, and define the loading functionality so that it will load classes with other naming conventions. then register your own autoloader with Zend_Loader. like this:
Zend_Loader::registerAutoLoad('myLoader',true);
I've put the contents of library of the archive of HTMLPurifier in my library path. So I have this directory structure :
library/
HTMLPurifier\
HTMLPurifier.auto.php
...
HTMLPurifier.safe-includes.php
And then I put this on top of the file where I'm using the HTMLPurifier :
require_once 'HTMLPurifier.safe-includes.php';
Ugly, but it's working.