I have AclManager class in library/AclManager.php and this code in public/index.php
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
require_once 'Zend/Application.php';
Zend_Loader::loadClass("AclManager");
but the server throws an internal error (500). Why?
Building on manyxcxi's answer - here is how I would manage this:
First, give your AclManager a "namespace". You can do this by renaming the class to {Namespace}_AclManager and moving it to library/{Namespace}/AclManager.php.
Now it is as simple as registering your namespace with the Autoloader which you can do as manyxcxi pointed out OR you can simply add it to your application.(ini|xml|etc).
Here is an example application.xml:
<autoloaderNamespaces>
<{Namespace}>{Namespace}_</{Namespace}>
</autoloaderNamespaces>
... and here is the equivilant in application.ini:
autoloaderNamespaces[] = "{Namespace}_"
Obviously, you'll want to replace {Namespace} in everything above with your own custom namespace - like App or maybe Aegidius.
It's all about setting up your bootstrap for it to be auto loaded correctly.
This should be the first function of your bootstrap.php file:
// resources are bootstrapped in order, so be sure dependencies for
// resources are listed first. you can explicitly bootstrap a resource by
// calling $this->bootstrap('resource').
protected function _initAutoload()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
// enable classic PEAR-like class autoloading
$autoloader->setFallbackAutoloader(true);
$autoloader->registerNamespace('Ashurex_');
}
Replace Ashurex with your namespace, but make sure to keep the trailing _.
The set fallback auto loader will allow ZF to pick up classes in a more 'loose' pattern. You don't have to define a bunch of namespaces and such. There may be times when you don't want this set to true, but for just getting going it helps.
-- EDIT --
After you define your namespace, you will want to put your classes in your project/library/namespace folders. For instance, if I have a class named Ashurex_Testing_MyClass I would have it stored at project/library/Ashurex/Testing/MyClass.php. It's important to recognize that _ in a classname are like dots in Java. They're essentially directory/file paths.
Remove the comma of:
get_include_path(),
Update: Apparently this could not solve the problem ;-)
Related
I have 3 files.
vehicleClass.php
motorbikeClass.php (extends vehicleClass)
index.php
My question is... How do I connect all 3. On my index page, do I have to have
include 'classes/vehicleClass.php';
Do I need to have an include for the motorbike class or will the extend (inheritence) cover this?
You can let php autoload your files, by registering your own autoload function. If you have, in example, all your class files in the directory DOCROOT . 'classes' (DOCROOT being your document root), you can use a function like this (example):
function class_loader($class) {
include DOCROOT . 'classes/' . $class . '.class.php';
}
spl_autoload_register('class_loader');
Now if you try to create an object of class 'foo', it will try to include the file DOCROOT . '/classes/foo.class.php' before creating the object. You might want to extend the function a bit, eg. to lowercase file names (include DOCROOT . 'classes/'. strtolower($class) .'.class.php';). This way you can place class Foo in foo.class.php.
Short answer: a class that is extending (or otherwise using) another class must already have defined the parent class before the definition of the child class. Your assumption is correct, your VehicleClass must be included (or better, require'd) prior to your definition of MotorBike class.
However, most frameworks don't go about and include every depedency before all class definitions. This would become unwieldy on any system that has any amount of complexity to it. Instead, the developers of PHP have provided methods for autoloading classes. Using spl_autoload_register will allow you to write a function that will attempt to load in the source file for a given class whenever it is referenced but a definition for it has not yet been found.
Furthermore, once you get a system together that becomes complex, you don't want to store all of your files in a single place. Most frameworks leverage the filesystem and namespaces to help better organize all of their classes. Because of this, the PSR-0 standard was developed in order to help facilitate autoloading between frameworks. Take a look at this question for examples of PSR-0 compliant autoloaders.
Example of PSR-0 compliant class:
<?php namespace Vendor\Package;
class ClassName { }
This file would live in the filesystem at /Vendor/Package/ClassName.php
What you have to do is include 2 files in the index.php.
For example, your index.php page could be something like this.
<?php
require 'classes/vehicleClass.php';
require 'classes/motorbikeClass.php';
// Assuming your class name is MotorBike
$motorBike = new MotorBike();
// And just call the method you want, for example If you have a method called bikeName
echo $motorBike->bikeName();
?>
I hope you get an idea now.
P/S: I prefer require over include. :) Include() should work fine too.
I want to try Yii, but I don't want use it as my main framework. In other words, I want to use my own framework while also using some of Yii's features. I figured that in order to be able to instantiate Yii's classes from my application, I'd just need to register Yii's autoloader from my application, probably in a way similar to this:
spl_autoload_register
(
function ($classname)
{
YiiBase::autoload($className);
}
);
Of course, I'm gonna need to require or include the YiiBase class, so before I call the previous function, I do this:
$yiiBase = $_SERVER['DOCUMENT_ROOT'].'/yii/framework/YiiBase.php';
require_once($yiiBase);
But I get a "Cannot redeclare class YiiBase" error. What am I missing?
1) Do not include YiiBase.php direcly, include yii.php. Because yii.php contains a class Yii which is used in all over framework code (even in YiiBase methods).
$yii = $_SERVER['DOCUMENT_ROOT'].'/yii/framework/yii.php';
require_once($yii);
( and YiiBase.php is included in yii.php by default)
2) register your autoload handler in this way.
(Yii has built-in functionality to add custom autoload handlers ).
$my_autoload = function($class) { ... };
// OR
// $my_autoload = array('MyClass', 'my_autoload')
YiiBase::registerAutoloader($my_autoload, true);
The second parameter true tells whether to append/prepend the new autoloader after/before the default Yii autoloader
if the YiiBase.php included, then Yii's default autoloader will also gets included. No need to call YiiBase::autoload() explicitly in you code. Ref: check the last line in YiiBase.php file
You can take a look at some approaches of people integrating Yii with wordpress, you may not need to do ->run() the application, unless you need the controllers/routing, it also depends on what parts of the framework you pretend to use.
To do it for Yii2, this article explains it under the heading "Using Yii in Third-Party Systems" in the middle of the page.
Here is the relevant part to include on startup of your external application:
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
$yiiConfig = require(__DIR__ . '/../config/yii/web.php');
new yii\web\Application($yiiConfig); // Do NOT call run() here
Obviously, __DIR__ . '/../ may need to be adjusted to fit your directory layout.
I've been playing a bit with the Symfony class loader (read about others, the concept and started implementing).
I've read http://symfony.com/doc/current/components/class_loader.html as well
I've changed the directories structure to fit and all. Here's a small source-code which fails:
Filename: test.php , dir: C:/test/BE/src/main.php
<?php
define('BASE_DIR','/../..');
echo BASE_DIR.'/BE/libs/symphony/component/ClassLoader/2.1.0/UniversalClassLoader.php';
require_once BASE_DIR.'/BE/libs/symphony/component/ClassLoader/2.1.0/UniversalClassLoader.php';
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->registerNamespace('App\Location\Location', 'Location/Location');
// You can search the include_path as a last resort.
$loader->useIncludePath(true);
$loader->register();
use App\Location\Location;
new App\Location\Location(); //Fatal error: Class 'App\Location\Location' not found in C:/test/BE/src/main.php
file name: Location.php , dir: C:/test/BE/src/Location/Location.php
namespace App\Location;
class Location{
private $lat;
private $lng;
}
By registering your namespace as follows:
$loader->registerNamespace('App\Location\Location', 'Location/Location');
autoloader will look for the App\Location\Location class in the Location/Location/App/Location/Location.php file. Note that file/directory names are case sensitive.
First parameter of registerNamespace() is either a namespace or part of the namespace. Second parameter is a path to a PSR-0 compliant library. That means that directory structure inside that path should follow PSR-0 standard. This path is not used when calculating path for given namespace.
If you want to keep your current directory structure following code should work for you:
$loader->registerNamespace('App', __DIR__);
I'd rather put your source code in one common directory, for example src:
src/
App/
Location/
Location.php
And then register namespaces as follows:
$loader->registerNamespace('App', 'src');
By doing that you're simply telling an autoloader that it should look for App namespaced classes in the src folder. Note that it will work for every class in the App namespace (following PSR-0).
Some time ago I wrote a blog post about the Symfony autoloader. You might find it helpful: Autoloading classes in an any PHP project with Symfony2 ClassLoader component
Since you plan to use Pimple and some of the Symfony components take a look at Silex microframework. Might be it's something you need to implement.
You use:
$loader->registerNamespace('Pimple', BASE_DIR.'/BE/libs/Pimple/1.0.0/lib/Pimple');
To register the Pimple namespace for Pimple is useless, as Pimple is one class in the default namespace (it has no namespace).
The UniversalClassLoader does only work for PSR-0 and PEAR compatible directory layouts. If your layout is not, you should not expect it to work nor to be documented that it works for incompatbile layouts.
Explicit Note: You are using the wrong tool to do the job. All file layouts you have shared over the last hours are incompatible with UniversalClassLoader.
Apart from that if you give the wrong values for it's parameters, the class just won't work.
I am using an aliased and namespaced class in a parent class successfully but it doesn't seem to be available in the child class. The actual error is from the autoloader. The weird thing is that the function does work in the parent class and loads fine. How can I make a class brought in by use available in subclasses?
edit: the recipes are stateless -- would it make sense to make them singletons in Base and then reference them as members in the child class MyTest?
I have the two files:
Base.php:
namespace selenium;
use selenium\recipe\Cms as Cms;
class Base extends \PHPUnit_Framework_TestCase
{
public function __construct()
{
Cms::staticfunc(); //works fine
}
}
MyTest.php:
class MyTest extends \selenium\Base
{
public testMyTest()
{
Cms::staticfunc(); //errors here
}
}
From comment:
i was hoping for a way to cascade the use without duplicating that line among the 20 or so child classes
That is one of the biggest issues I have with PHP namespacing, that you have to call use for every file the current script needs access to. It's the same situation we used to face having to call require_once 20 times on some scripts in order to bring in the necessary libraries.
What I prefer to do is namespace my files (as they reside on the filesystem, like Zend Framework does) and use an autoloader to avoid the whole mess. I currently use ZF autoloader, which can be used outside of the framework, or you can also use the vanilla PHP implementation using SplAutoload.
-- Update --
I have a library which I have written over the last few years which is namespaced as Hobis_Api, and are located on the filesystem with the same convention; ~/projects/projects/dp/hobis/lib/Hobis/Api/*. In order to register the namespace with Zend_Loader I do the following:
// Be sure to set the include path to include the Zend and Hobis_Api files
// Not sure how your setup is, but would look something like:
set_include_path(get_include_path() . ':' . DIRNAME(__FILE__));
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace(
array(
'Hobis_Api_'
)
);
Normally the above code would go into some bootstrap file, which you can call from a centralized script in order to register the autoloader, once.
Now, if your include path is set correctly, anytime you reference Hobis_Api_* it will be autoloaded for you, so you don't need to call use or require_once, example usage:
// SomeScript.php
// Notice no requires
// I can make a call to Hobis_Api_Image without error
$image = Hobis_Api_Image;
$image->setHeight(400);
I've started to document myself regarding Zend Framework as I will soon start using it in production. Everything was working fine, until I started to use and work with models :).
The default location for models, based on Zend recommendations, is application/models/DbTable, where all the models will be thrown in. This location forces me to name a model like Application_Model_DbTable_Actors. For me, this is a very long name for a model and not a easy to use one.
The directory structure that I want to obtain looks something like this:
application/
models/
actors/
ActorsMapper.php
Actor.php
books/
BooksMapper.php
Book.php
etc.
So all my models will reside under the models directory, but grouped in their own directories.
Naming for each class should be ActorsMapper or Actor (They will both extend Zend_Db_Table or Zend_Db_Row).
I am aware of the fact that in my controllers if I instantiate a model using something like $actors = new ActorsMapper() I will get a Fatal error: Class not found and that why I'm asking for your help with this.
To solve this issue I tried to add my models directory to the include_path:
first try
added includePaths.models = APPLICATION_PATH "/models" to application.ini
but this one doesn't even add it to the include path
second try:
explicitely added the path using set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
realpath(APPLICATION_PATH . '/models'),
get_include_path(),
)));
but even if this adds that path among the included ones, the error still persists.
I've seen this naming of models in the official documentation of the Zend_Db_Table, but I couldn't find anything related to autoloading them.
Thank you all for any solutions.
p.s. zend framework version is 1.11.1
Zend Framework has a build in autoloader. It works by mapping the class name to the directory tree, so Application_Model_Actors_Actor will get mapped to
Application\
Models\
Actors\
Actor.php
Unfortunately, you cannot change this. The feature you are looking for is namespacing but it is only supported (and is in fact one of the major features) of Zend Framework 2 which is still being developed.
Try to extend Zend_Application_Bootstrap_Bootstrap then you can try this one
$loader = $this->getResourceLoader();
$loader->addResourceType('books', 'models/books', 'Model_Book');
$loader->addResourceType('actors','models/actors','Model_Actor');
I am also trying to implement this kind of implementation in a observer pattern.