Disambiguating a PHP namespace used by two vendors - php

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();

Related

Yii 2: Using a class from inside a require_once

I'm using a 3rd party extension like so:
(This is inside my controller)
require_once Yii::$app->basePath.'/vendor/campaignmonitor/createsend-php/csrest_subscribers.php';
$wrap = new CS_REST_Subscribers($list_id, $auth);
However, this is returning an error that CS_REST_subscribers class is not found.
How do I use this class correctly when the class is inside the file. Unfortunately this extension is older and is not namespaced.
You need to install it using composer with the following command
composer require "campaignmonitor/createsend-php" "6.0.0"
It uses the simplest way, i.e autoloads each class separately. we define the array of paths to the classes that we want to autoload in the composer.json file and if you see the vendor/campaignmonitor/createsend-php/composer.json file inside the package directory
"autoload": {
"classmap": [
"csrest_administrators.php",
"csrest_campaigns.php",
"csrest_clients.php",
"csrest_general.php",
"csrest_events.php",
"csrest_lists.php",
"csrest_people.php",
"csrest_segments.php",
"csrest_subscribers.php",
"csrest_templates.php",
"csrest_transactional_classicemail.php",
"csrest_transactional_smartemail.php",
"csrest_transactional_timeline.php"
]
}
so you won't need the include or require statement, you can directly call any class you want for instance adding the following lines inside your action or view
$authorize_url = CS_REST_General::authorize_url(
'1122',//'Client ID for your application',
'http://example.com/redirect-page',//Redirect URI for your application,
'ViewReports'//The permission level your application requires,
);
print_r($authorize_url);
prints the following
https://api.createsend.com/oauth?client_id=1122&redirect_uri=http%3A%2F%2Fexample.com%2Fredirect-page&scope=ViewReports
For knowledge base if you want to use a Third-party code that is not using autoloader or psr4 you can go through the Yii tutorial

Don't understand php namespace, how can I call function from another class?

I am trying to use Azure storage for php, the setup steps are to include the namespace, include composer autoload and then use the azure classes, so I have the following. However further down I use the class Microgrid and it's not found because of the namespace, it is in a different directory. How can I use other classes that aren't part of that namespace? Also, what's the correct way to specify your namespace path? It's in a different directory relative to the page this is for and the one I am using is not at the root, should I start at the root?
namespace MicrosoftAzure\Storage;
use \MicrosoftAzure\Storage\Common\ServicesBuilder;
use \MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions;
use \MicrosoftAzure\Storage\Blob\Models\PublicAccessType;
use \MicrosoftAzure\Storage\Common\ServiceException;
require_once '/var/www/html/vendor/autoload.php';
$action = MicroGrid::GetParameter('action');
ClassNames are considered relative to the current namespace unless they start with a \
This means that inside the MicrosoftAzure\Storage namespace you can use the relative namespace for class.
If you want to call a class from a different namespace you should call fully-qualified name for it like
$action = \MicrosoftAzure\WhereEver\MicroGrid::GetParameter('action');
or use the name space or unique class with fully-qualified name
use \MicrosoftAzure\WhereEver;
or
use \MicrosoftAzure\WhereEver\MicroGrid;
then:
$action = MicroGrid::GetParameter('action');
Edited to make it clear
namespaces allow us to avoid naming collisions and to alias long
names caused by avoiding naming collisions.
it depends to your autoloader for a simple example I create a new project and make this autoloader in the index.php located at root directory
function __autoload($className){
//if it's a full name with windows style slashes correct the path
$file_name = str_replace('\\', '/', $className);
require_once("vender/src/".$file_name.".php");
}
when I call $date = new \App\Utility\Date(); autoloader will require this file:
verdor/src/App/Utility/Date.php
and in Date.php I used this name space namespace App\Utility;
PHP does not provides a class autoloader out of the box.
There are many autoloaders for PHP, and the most common autoloader standard is the PSR-4, used by many frameworks and applications.
If you are not using an autoloader, you should require every class file (and recursive dependencies) before using it.
Azure uses Composer Autoloader and PSR-4.
You should use Composer Autoloader on your project, then import your class from the right namespace (you are not importing it on your example code)
A namespace basically groups your classes together. You can use something outside your namespace by explicitly using it e.g.
namespace App;
use Illuminate\Database\Eloquent\Model;
In this case I'm 'grouping' this and other classes in a namespace called 'App' but I want to use 'Model' provided by Eloquent (the Eloquent class having a namespace of 'Illuminate\Database\Eloquent') in this class.
If 'Microgrid' is not part of your current namespace, you'll need to explicitly add its namespace in your 'use' statements.

laravel 5 Class implements Iterator

I am trying to integrate an existing library into Laravel5 which itself is not namespaced and uses its own classes in subfolders using require.
I have placed it at 'app/API/libname/mainlibclass.php'.
with sibling directory at 'app/API/libname/toolkit' which contains the classes the library uses.
Calling from a Laravel controller I am unable to create the class using a require statement (correct?) before
$objectinstance=new Mainlibclass();
so in the main Laravel app I have
use app/API/libname/Mainlibclass
then later the usual
$objectinstance=new Mainlibclass();
In the existing library and each of its own used classes I set
namespace app/API/libname
and 'use' where needed.
I now have no class not found but one of the files uses 'implements Iterator' - I am getting error Interface 'App\API\libname\Iterator' not found.
Try adding \ in front of that so it looks like this:
class ABC implements \Iterator {
Edit:
I think it would be better practice to keep external non-psr-4/0 libraries untouched (for easier update if needed in future) and outside of app/ directory.
You could use composer classmap autoload feature for this.

directory structure and use in php

I was just going through the laravel documentation HERE and came across the following peice of code ::
<?php
namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->singleton('Riak\Contracts\Connection', function ($app) {
return new Connection(config('riak'));
});
}
}
I am new to use in php , just learnt how it functions a few days ago , now does use , when used with a framework like laravel, where one class can be in a directory totally different from another , need to specify the directory structure too ?
I.E. can directory structure impact the way use is used ?
The use statement in PHP (when used outside of a class) is used to import a class from another namespace. Namespaces and folder structure do not necessarily correspond, but it is generally pretty close.
The autoloader used by Laravel, and most other modern PHP applications is part of the Composer package manager. Composer in turn supports multiple namespace standards, most notably PSR-0 and its successor, PSR-4.
In a composer.json file, you'll generally specify a namespace to autoload, and a base directory like so:
{
"autoload": {
"psr-4": {
"My\\Namespace\\": "src"
}
}
}
Any classes in the src/ directory should be in the My\Namespace directory. Classes in src/Model should have the namespace My\Namespace\Model and so on.
What a lot of PHP libraries use nowadays is called an autoloader which may mirror the directory structure, but does not necessarily have to.

Using two different directories for same namespace in Zend Framework 2 - howto?

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.

Categories