PHP: check if 'use' a valid class - php

If I fail to include a file in PHP, it reports.
But how do I know I successfully import GuzzleHttp\Cookie\CookieJar Class ?
And how do I know it is a valid Class ?
namespace GuzzleHttp;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Promise;

It is important to note that the use statement will NOT include that class, therefore it would be impossible to detect if this (statement) was succesful.
However if you would try to instantiate one of the imported classes and the import would have failed, you'd get an error.
You usually use an autoloader for this, such as composer, but you can also write one yourself. More information about this at this page: What is Autoloading; How do you use spl_autoload, __autoload and spl_autoload_register?

Nowadays most application use autoload feature from composer.
You can check if a class exist via code:
if (!class_exists(CookieJar::class)) {
throw new Exception('class not found');
}
but class_exists probably more useful when you do dynamic stuff.

Related

Trying to add a library to Symfony using composer

I am trying to add the following library (link) to my Symfony project using composer.
I have run
composer require jaggedsoft/php-binance-api
without a problem but I am getting the following error when loading the page.
Attempted to load class "API" from namespace "App\Controller\Binance".
Did you forget a "use" statement for another namespace?
public function index(){
require '../vendor/autoload.php';
$api = new Binance\API("<api key>","<secret>");
}
Now i am guessing that I need to add a use statement but I am a bit stuck on what it is that I need to add.
To reiterate what I suggested in the comments (options 1 and 3 below):
The namespace of your file - although not explicitly written in your post - is:
App\Controller
without any use statement, the new Binance\API(...) is interpreted as:
App\Controller\Binance\API
which is the concatenation of App\Controller (your namespace) and Binance\API (the classname used).
which of course is not what you want to use, since this is something you tried to include from the binance package. This also explains the error message
Attempted to load class API from namespace App\Controller\Binance. Did you forget a use statement for another namespace?
which is exactly what went wrong. PHP tried to load App\Controller\Binance\API which is class API from namespace App\Controller\Binance.
Now there are A few different ways to fix this:
add use Binance; in the header of your file, then you can use new Binance\API(...)
add use Binance\API; in the header of your file, then you can use new API(...)
don't add a use statement, then you can use new \Binance\APi(...)
add use Binance as Something; in the header of your file, then you can use new Something\API(...); (aliasing the parent namespace Binance as Something may resolve name collisions)
add use Binance\API as BinanceApi; in the header of your file, then you can use new BinanceApi(...);
You decided to use option 1. Which is preferable, if the class (API in this case) isn't very expressive or unique on its own - so is option 5. However, if you use more classes from the Binance namespace, option 1 is preferable.
Option 3 will always work (and might seem preferable if any of the other options seem overkill for some reason) - you can actually get by without any use statement at all, but it can get frustrating to read and write.
Overall, all options are viable and it comes to taste which one to use. Mixing those options may lead to confusion. Inside Symfony I've mostly seen option 2 with the occasional alias (use ... as ...;), especially when using DoctrineORM annotations or when extending some Class which has the same Class name but in a different namespace:
namespace [package1];
use [package2]\[ClassName] as Base[ClassName];
class [ClassName] extends Base[ClassName] { ... }
I hope this explanation helps. The php docs for namespaces are actually helpful, when you understand the core concept of namespaces.

In yii2, how do I autoload my own PHP classes?

Inside my Controller I want a function to use mpdf e.g.
public function actionPdf(){
include("MPDF57/mpdf.php");
$mpdf=new mPDF('c');
$mpdf->SetDisplayMode('fullpage');
$mpdf->WriteHTML("<h1>Hello World!</h1>");
$mpdf->Output('filename.pdf', 'F');
}
}
This does not work, and throws an error:
Class 'app\controllers\mPDF' not found
What should I do If I want to autoload the class
(a). Just for this Controller Action
(b). To make it usable everywhere just by using the use statement.
I know it has to do something with namespaces but don't know how do I define a namespace, and where do I place this MPDF57 folder and then make it accessible.
I also tried this :
$name = "MPDF57/mpdf.php";
spl_autoload_register(function ($name) {
var_dump($name);
});
But this didn't work either. throws the same error when I call my controller Action.
Here is the namespace declaration and use statements inside :
namespace app\controllers;
use Yii;
use app\models\Regs;
use app\models\Voters;
use app\models\RegsSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use \yii\web\Response;
use yii\helpers\Html;
use kartik\mpdf\Pdf;
Yii has already had autoloader, you do need nothing to load your class.
Just create your class with correct namespace and it will be loaded where are you using it only.
Namspace should represent real path to PHP file. PHP file name and class name should be same.
You should simply use mpdf/mpdf package :
Install it using composer :
composer require "mpdf/mpdf" ">=6.0.0"
Use it like this :
$mpdf = new \mPDF();
Or you can use a yii2 extension like this one : https://github.com/kartik-v/yii2-mpdf
I've faced such problems in one of my previous projects. I'm not good at PHP or Yii2 - so follow my guide on your own risk :)
When you you add use path\to\ExternalLibrary that means the interface is ready to use inside current class (e.g. CurrentController.php).
That means your application knows how to bring your path to it's stage.
E.g. use common\models\Post lets you directly to use Post class, as $posts = new Post;
So if your library contains only one file, just put is some "canonic" path. To common\models\ for example. So you can use it like any other model interface.
But for sake of your project put it on vendor folder. Then install a random library with composer. And observe which files are modified (1-3 generally). Also try to understand the modification logic. When you get sure that you've grasped everything, copy and paste these parts and change the paths, names, etc. for your library.
The best way, I think, is to make your library PSR-4 compatible and ship it as a PHP package. Thus, others can also benefit from your work.
There are lots of guides about making php packages.
http://sitepoint.com/starting-new-php-package-right-way/
https://knpuniversity.com/screencast/question-answer-day/create-composer-package
http://jessesnet.com/development-notes/2015/create-php-composer-package/
http://culttt.com/2014/03/12/build-php-package/
If you are planning to be a good PHP developer, I recommend to look up Josh Lockhart's "Modern PHP: New Features and Good Practices" book ( free pdfs are available :) ). That will help you to understand the fundamentals of OO PHP including namespaces, interfaces etc. So, you will be able to handle such problems in modern way.

Use namespace in global context with autoloader

Due to an external library I have to use namespaces. Since I havn't used namespaces before I realised using a namespace won't work with my __autoload(). The code below will say
Failed to load class Foo\Bar\NamespaceClass, please update the autoloader!
How may I fix this? The Foo class is just a wrapper around the external library.
// This is my autoloaded class file
use Foo\Bar\NamespaceClass;
class Foo
{
public function Bar()
{
$namespaceClass = new NamespaceClass();
}
}
// This is a separate file calling my class "Foo" from global namespace
$myAutoloadedClass = new Foo();
$myAutoloadedClass->Bar();
I don't think you understand how namespaces work. I recommend you to read articles about that, for instance this one on nettuts+.
Furthermore, I recommend you to use the PSR-0 standards. Almost all 3rd-party libraries follow that and they come in with a autoloader which you can use directly.
At last, you said you use an external library. Have you take a look at composer? That a really great tool to help you autoloading, updating and using external libraries. I recommend you to watch the great screencast of KnpUniversity: "The Wonderfull World of Composer"
You should update the autoloader.

How does the keyword "use" work in PHP and can I import classes with it?

I have a file with a class Resp. The path is:
C:\xampp\htdocs\One\Classes\Resp.php
And I have an index.php file in this directory:
C:\xampp\htdocs\Two\Http\index.php
In this index.php file I want to instantiate a class Resp.
$a = new Resp();
I know I can use require or include keywords to include the file with a class:
require("One\Classes\Resp.php"); // I've set the include_path correctly already ";C:\xampp\htdocs". It works.
$a = new Resp();
But I want to import classes without using require or include. I'm trying to understand how use keyword works. I tried theses steps but nothing works:
use One\Classes\Resp;
use xampp\htdocs\One\Classes\Resp;
use htdocs\One\Classes\Resp;
use One\Classes;
use htdocs\One\Classes; /* nothing works */
$a = new Resp();
It says:
Fatal error: Class 'One\Classes\Resp' not found in C:\xampp\htdocs\Two\Http\index.php
How does the keyword use work? Can I use it to import classes?
No, you can not import a class with the use keyword. You have to use include/require statement. Even if you use a PHP auto loader, still autoloader will have to use either include or require internally.
The Purpose of use keyword:
Consider a case where you have two classes with the same name; you'll find it strange, but when you are working with a big MVC structure, it happens. So if you have two classes with the same name, put them in different namespaces. Now consider when your auto loader is loading both classes (does by require), and you are about to use object of class. In this case, the compiler will get confused which class object to load among two. To help the compiler make a decision, you can use the use statement so that it can make a decision which one is going to be used on.
Nowadays major frameworks do use include or require via composer and psr
1) composer
2) PSR-4 autoloader
Going through them may help you further.
You can also use an alias to address an exact class. Suppose you've got two classes with the same name, say Mailer with two different namespaces:
namespace SMTP;
class Mailer{}
and
namespace Mailgun;
class Mailer{}
And if you want to use both Mailer classes at the same time then you can use an alias.
use SMTP\Mailer as SMTPMailer;
use Mailgun\Mailer as MailgunMailer;
Later in your code if you want to access those class objects then you can do the following:
$smtp_mailer = new SMTPMailer;
$mailgun_mailer = new MailgunMailer;
It will reference the original class.
Some may get confused that then of there are not Similar class names then there is no use of use keyword. Well, you can use __autoload($class) function which will be called automatically when use statement gets executed with the class to be used as an argument and this can help you to load the class at run-time on the fly as and when needed.
Refer this answer to know more about class autoloader.
use doesn't include anything. It just imports the specified namespace (or class) to the current scope
If you want the classes to be autoloaded - read about autoloading
Don’t overthink what a Namespace is.
Namespace is basically just a Class prefix (like directory in Operating System) to ensure the Class path uniqueness.
Also just to make things clear, the use statement is not doing anything only aliasing your Namespaces so you can use shortcuts or include Classes with the same name but different Namespace in the same file.
E.g:
// You can do this at the top of your Class
use Symfony\Component\Debug\Debug;
if ($_SERVER['APP_DEBUG']) {
// So you can utilize the Debug class it in an elegant way
Debug::enable();
// Instead of this ugly one
// \Symfony\Component\Debug\Debug::enable();
}
If you want to know how PHP Namespaces and autoloading (the old way as well as the new way with Composer) works, you can read the blog post I just wrote on this topic: https://enterprise-level-php.com/2017/12/25/the-magic-behind-autoloading-php-files-using-composer.html
You'll have to include/require the class anyway, otherwise PHP won't know about the namespace.
You don't necessary have to do it in the same file though. You can do it in a bootstrap file for example. (or use an autoloader, but that's not the topic actually)
The issue is most likely you will need to use an auto loader that will take the name of the class (break by '\' in this case) and map it to a directory structure.
You can check out this article on the autoloading functionality of PHP. There are many implementations of this type of functionality in frameworks already.
I've actually implemented one before. Here's a link.
I agree with Green, Symfony needs namespace, so why not use them ?
This is how an example controller class starts:
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class WelcomeController extends Controller { ... }
Can I use it to import classes?
You can't do it like that besides the examples above. You can also use the keyword use inside classes to import traits, like this:
trait Stuff {
private $baz = 'baz';
public function bar() {
return $this->baz;
}
}
class Cls {
use Stuff; // import traits like this
}
$foo = new Cls;
echo $foo->bar(); // spits out 'baz'
The use keyword is for aliasing in PHP and it does not import the classes. This really helps
1) When you have classes with same name in different namespaces
2) Avoid using really long class name over and over again.
Using the keyword "use" is for shortening namespace literals. You can use both with aliasing and without it. Without aliasing you must use last part of full namespace.
<?php
use foo\bar\lastPart;
$obj=new lastPart\AnyClass(); //If there's not the line above, a fatal error will be encountered.
?>
Namespace is use to define the path to a specific file containing a class e.g.
namespace album/className;
class className{
//enter class properties and methods here
}
You can then include this specific class into another php file by using the keyword "use" like this:
use album/className;
class album extends classname {
//enter class properties and methods
}
NOTE: Do not use the path to the file containing the class to be implements, extends of use to instantiate an object but only use the namespace.

PHP namespaces problems

I'm trying to use an external library.
Because there are some conflicts I'm using namespaces ( php 5.3 )
The goal is not to change the external library at all ( just adding the namespaces at the top)
The problem is inside the library there are several situations that don't work
is_a($obj,'3thpartyclassname') only works if I add the namespace in front of 3thpartyclassname
the 3th party uses native classes but they don't work only if I apped the global space (new \Exception)
Any way to make this work with no modifications ?
Update
use \Exception as Exception; fix problem 2
I only have problems with is_a and is_subclass_of. Both of them need the namespace and ignore the current namespace.
No, you have to do some modifications
namespace My\Own\Namespace; // declare your own namespace
use My\ThirdParty\Component; // import 3rd party namespace
$component = new Component; // create instance of it
var_dump(is_a($component, 'Component')); // FALSE
var_dump($component instanceof Component); // TRUE
The is_a and is_subclass_of methods require you to put in the fully qualified classname (including the namespace). To my knowledge, there is no way around that as of PHP 5.3.5. Using instanceof should solve both bases though.
Importing the native classes, like Exception should also work, e.g.
namespace My\Own\Namespace;
use \Exception as Exception;
throw new Exception('something broke');
See the chapter on Namespace in the PHP Manual for further information.
I don't think there's any way to make is_a() respect relative namespaces (such as the current namespace or a namespace imported with a use command). This is because it takes a string argument and executes in a different context. You'd need to switch to the instanceof syntax instead. So no, I don't think that this will help you avoid collisions between libraries which both are written against the global namespace, you'll still have to find instances like this and address them directly.

Categories