I am trying to define a namespace/autoload structure for multiple projects. Each project has a library. The consists classes which will be extended for each project. Here is the content of one project
/project1
/lib
parentclass.php
autoloader.php
index.php
myclass.php
parentclass.php:
<?php
class parentclass{
}
?>
myclass.php
<?php
class pr1_myclass extends parentclass{
}
In index.php i will define a global array with settings which will be used in the autoloader:
<?php
$settings = array(
'dir' => __DIR__;
'lib_dir' => 'project1_dir',
'lib_namespace' => '??',
);
$project_prefix = 'pr1';
$GLOBALS['projectsAutoloadSettings'][$project_prefix] = $settings;
The autoloader will be something like this:
<?php
function my_autoloader($className){
$parts = explode('_',$className);
if(isset($GLOBALS['projectsAutoloadSettings'][$parts[0])){
$settings = $GLOBALS['projectsAutoloadSettings'][$parts[0]];
//I now have settings array for this project. From here i can include the right file:
include($settings['dir'].'/'.$parts[1].'.php');
}else{
//Need to include library file. This is part of my problem. I dont know how to handle this
}
}
spl_autoload_register('my_autoLoader');
This was the structure for 1 project. I have multiple projects. The /lib directory in each project is the same.
Now i need a namespace structure which will work in both 2 environments mentioned below, while only having to change the $settings.
Local developer environment. Here a single /lib should be used for all projects. So for example a class from project 3 will extends a parent class from the library from project 1.
Active environment. Here each class in a project extends the library from its project. So a class from project 3 will always extend a parent class from project 3.
Multiple projects can be active at a single web page. I figure i need namespaces for this, but my main question is that the /lib directory is always the same code (copy pasted). I want to keep it that way, as far as i know it is not possible to have a different namespace for /project3/lib than for project1/lib.
How can i still achieve my goal. I am interested in the code-structure, things like autoloader code (classname to filename) i will figure out myself.
EDIT: An edit with an example of a problem i am facing, which seems not clear if i look at the comments.
It is in live (not developer) mode. I have a second project with the same structure as project one, but instead with this class in /project2/someclass.php
<?php
class pr2_someclass extends parentclass{
}
?>
Now this is my code for running both projects:
<?php
//project 1 start:
$myClass1 = new pr1_myclass();
//This loads pr1_myclass which is an extension from the parent class in project1/lib/parentclass.php.
//So far so good
//Project 2 start
$myClass2 = new pr2_someclass();
//Here the problem occurs. This class loads pr2_someclass, but it will be an extension of project1/lib/parentclass.php instead of project2/lib/parentclass.php.
?>
Since both lib directories are identical, you might think i just should use the same library for both projects. However, there can be more than a year between two projects so it might be possible that the two libraries are not exactly identical.
Related
I have two composer packages in my app that are creating a namespace collision in a third party module. I'm trying to determine the least disruptive way to disambiguate within the third party module so that I don't have to fork the packages.
First package from Vendor X, located in a directory structure like
path/to/vendor/VendorX/MailPackageA:
<?php
namespace MailPackageA;
class Mail {
...
}
My existing third-party module, which lists VendorX/MailPackageA as a dependency, located in structure like
moduleDirectory/MailingModule/mail.inc:
<?php
class ThirdPartyMailMod implements CoreMailSystem {
$mail_object = new MailPackageA\Mail();
. . .
}
For slightly different mailing functionality used in another module, I had to add a package that uses the same namespace identifier (MailPackageA), but from a different vendor, "VendorY". Like VendorX/MailPackageA, it's placed in a directory structure like
path/to/vendor/VendorY/MailPackageA:
<?php
namespace MailPackageA;
class Mail {
...
}
After adding this, I get errors in MailingModule/mail.inc at $mail_object = new MailPackageA\Mail(); because of the obvious namespace collision between MailPackageA\Mail classes from VendorY and VendorX.
Routes I've already been down, but failed:
Modify PSR-4 autoload rules in composer.json for either of the packages to create new namespaces. (I'm getting the sense my framework -- Drupal 7 -- does not respect the composer autoload rules, and simply defaults to stock php 7 namespacing), so I've abandoned that for the moment.
Try to add a vendor prefix alias to the MailingModule/mail.inc file with use.
<?php
use VendorX\MailPackageA;
\\ also tried the `use VendorX\MailPackageA as MailPackageA;` pattern to no effect
class ThirdPartyMailMod implements CoreMailSystem {
$mail_object = new MailPackageA\Mail();
}
I suspect that my app doesn't know about the vendor directories. Not sure how to force that.
What's the least disruptive way (i.e. minimal forking/patching) to make sure MailingModule only uses the MailPackageA\Mail class from Vendor X?
to overcome this problem use alias when you use the namespace .
use as keyword like this
use VendorX\MailPackageA as newPackageName;
and if you need to call the class from this namespace you will have it like this
$packageName = new newPackageName\Mail();
I am trying to organise my app a bit better by putting models and controllers in subdirectories. I thought it didn't matter if they were in subdirectories as long as the namespace is correct, but now that I've moved them I'm getting a class not found error.
I have tried running composer dumpautoload several times, but it's still not working.
Here is my directory structure:
App
Models
Managers
EntryStructure.php
FieldManager.php
Controllers
EntryControllers.php
Since I have made the new directory Managers and moved those two models in there, When I reference the FieldManager class from EntryStructure, I get the not found error.
Code in EntryStructure.php:
namespace Pascall\ICMS\Models;
use Pascall\ICMS\Models\FieldManager;
class EntryStructure
{
function index(){
new FieldManager(); // class not found
}
}
Code in FieldManager.php:
namespace Pascall\ICMS\Models;
class FieldManager {
//
}
Why is it not finding the FieldManager class when it is explicitly referenced in the use statement and they share the same namespace?
Your use should be
use Pascall\ICMS\Models\Managers\FieldManager;
instead
use Pascall\ICMS\Models\FieldManager;
If your Models directory follow the PSR-4 specifications, the namespace in both of your classes should follow the class file path, so it should be:
namespace Pascall\ICMS\Models\Managers;
Then, in EntryStructure class you should use:
use Pascall\ICMS\Models\Managers\FieldManager;
I have ZF2 module, and simultaneously I use Propel genereted models hosted in root-directory/generated-classes. Can I make them share selfsame namespace - like Bookstore or so?
From Zend\Loader\StandardAutoloader I see:
public function registerNamespace($namespace, $directory)
{
$namespace = rtrim($namespace, self::NS_SEPARATOR) . self::NS_SEPARATOR;
$this->namespaces[$namespace] = $this->normalizeDirectory($directory);
return $this;
}
so if I provide two directories in Module.php, the last will prevail.
There is also:
public function setFallbackAutoloader($flag)
{
$this->fallbackAutoloaderFlag = (bool) $flag;
return $this;
}
Can I resort to it and how do I leverage this option? Any other (better) options?
I wouldn't put my models directly in /your-application/root. This will be against ZF2's recommended directory scaffolding. Instead of that, I'd create a /FooModule/src/FooModule/Model directory and put all of my models inside this folder using namespace FooModule\Model namespace definition in model class.
Another detail is; trying to pointing two different directories for same namespace is absolutely bad idea. This will be against PSR-4 Autoloading Standard and lot of open source libraries & frameworks including Zend Framework 2 which heavily depends on this standard.
I would look at the problem from a different angle. Just ask: Why I need to point one of my namespaces to the two different directories?
I think actually you mean Domain Entities by "Propel generated models". If this is correct (i mean Bookstore) is an Entity rather than a Model. You may also want to read this great answer.
So, you can try to create an Entity namespace in your Application (or whatever) ZF2 module and write your Entity classes under a sub-namespace inside that. This is perfectly valid. For example:
Application\src\Entity\Bookstore.php - namespace is Application\Entity
Application\src\Entity\Book.php - namespace is Application\Entity
Application\src\Entity\Author.php - namespace is Application\Entity
Or this is also valid scenario too (Bookstore is a module):
Bookstore\src\Entity\Book.php - namespace is Bookstore\Entity
Bookstore\src\Entity\Author.php - namespace is Bookstore\Entity
In both example scenarios, Book.php and Author.php are your auto-generated domain entities and they shares same namespace while not conflicting ZF2 or PSR-4 autoloading mechanisms.
I tried to search here before creating this, but I couldn't find anything.
I have a simple project without modules and I'd like to load my models (which are inside application/models) without using any namespace and without usign any extra loading lines.
Basically what I want to do is to have my class Projects extends Zend_Db_Table_Abstract inside my models folder and to load it in my controller using $db = new Projects();
Is there anyway to do this? Is it recommended to use Model_Projects instead?
What If I had modules?
Edit:
I tried to use this without any other implementation and I got Class 'Projects' not found
It is because the Projects class is not autoloaded. Depending on your application namespace, (for example the default namespace 'Default') you have to name your class into something like: Default_Model_Projects
The ZF Docs reference 'Subclassing the Action Controller' (bottom of the page), but don't reference a standard place to put the new Action_Controller class.
Application_Module_Autoloader sets up pats for a bunch of things, but never controllers. I guess putting it on library/APPNAMESAPCE/Action/Contoller would work. But that seems a bit odd since every other application specific file is stored under application/.
The class gets autoloaded like any other class, there isn't a 'standard' place for it as such. So the question becomes, where do you want it to live?
The convention I usually follow in modular applications is to have most stuff in the modules, but register an app namespace and use application/models for 'core' type classes. So in your case, say your app namespace was Wordpress, you'd have:
class Wordpress_Controller_Action extends Zend_Controller_Action
{
}
and the file would live in application/models/Wordpress/Controller/Action.php.
To make this work you'll need application/models on your include path, and you'll want to init the standard autoloader with something like this (in your bootstrap class):
protected function _initAutoloader()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Wordpress_');
return $autoloader;
}
alternatively you could setup the above in application.ini.