php / composer not loading interface - php

I've banged my head against the wall for 3/4 of a day now and just can't see the error of my ways.
I'm creating (or trying to!) a simple package that consists of a couple of classes and 1 interface. This is in github at https://github.com/dnorth98/victoropsnotifier
Basically though, there's the following directory structure:
victoropsnotifer
src
Signiant
VictorOpsNotifier
Transport.php
VictorOpsNotifier.php
Transport is very simple:
<?php
namespace Signiant\VictorOpsNotifer;
interface Transport
{
// must POST the $message to the VictorOps REST endpoint
public function send(Messages\Message $message);
}
and the beginning of VictorOpsNotifier is
<?php
namespace Signiant\VictorOpsNotifer;
use GuzzleHttp\Client;
class VictorOpsNotifer implements Transport
{
protected $endpoint_url;
:
:
The problem comes when I try to instantiate a new object using
<?php
require_once 'vendor/autoload.php';
use Signiant\VictorOpsNotifier\Messages\CustomMessage;
use Signiant\VictorOpsNotifier\VictorOpsNotifier;
$voConfig = ['routing_key' => 'test',
'endpoint_url' => 'https://goo'];
$voHandle = new VictorOpsNotifier($voConfig);
I get back
PHP Fatal error: Interface 'Signiant\VictorOpsNotifer\Transport' not found in /tmp/djn/tests/vendor/signiant/
victoropsnotifier/src/Signiant/VictorOpsNotifier/VictorOpsNotifier.php on line 8
PHP Stack trace:
PHP 1. {main}() /tmp/djn/tests/test.php:0
PHP 2. spl_autoload_call() /tmp/djn/tests/test.php:12
PHP 3. Composer\Autoload\ClassLoader->loadClass() /tmp/djn/tests/test.php:0
PHP 4. Composer\Autoload\includeFile() /tmp/djn/tests/vendor/composer/ClassLoader.php:301
PHP 5. include() /tmp/djn/tests/vendor/composer/ClassLoader.php:412
What on EARTH am I missing? Composer is finding the package ok from my github repo and everying is in the vendor folder and looks ok. It looks like the namespaces match...so for some reason, it's just not loading the Transport.php file containing the interface.

It seems to be just a typo:
namespace Signiant\VictorOpsNotifer;
^ no `i` in here
but
use Signiant\VictorOpsNotifier\VictorOpsNotifier;
^ but here it is
same thing with the class name.
Also, you're declaring namespace Signiant as src/, but it's really src/Signiant
By the way, there's no need to declare this package as psr-0, better to use psr-4 insetad. Not a big deal, just for conformity.
P.S. The strange thing it was complaining about interface, while it shouldn't have to until it hit the class, and it certainly must not hit the class while there was a typo.
P.P.S You can easily avoid those typo errors by using a proper IDE, PHPStorm, for example. It would highlight name of the class as missing as long as there wre typos (and i don't even start talking about autocompletion).

class VictorOpsNotifer implements \Signiant\VictorOpsNotifer\Transport{}
try add namespace before your interface.

Related

Php 7.4.3 Class not found

This is my project path configuration
./create.php
/Install/Install.php
create.php
<?php
use Install\Install;
echo "Starting";
$install = new Install();
This gives me the error
PHP Fatal error: Uncaught Error: Class 'Install\Install' not found in /project/create.php:6
Install.php
<?php
namespace Install;
class Install
{
//whatever
}
Can someone explain me what is happening there ?
Obviously I guess that using a require_once line with my filename would probably fix the issue...but I thought using namespace and use import could prevent me from doing that like we do in classic framework like symfony / magento ?
I've seen some post speaking about autoloading, but i'm a little bit lost. Haven't been able to find a clear explanation on the other stack topic neither.
PHP compiles code one file at a time. It doesn't have any native concept of a "project" or a "full program".
There are three concepts involved here, which complement rather than replacing each other:
Namespaces are just a way of naming things. They allow you to have two classes called Install and still tell the difference between them. The use statement just tells the compiler (within one file) which of those classes you want when you write Install. The PHP manual has a chapter on namespaces which goes into more detail on all of this.
Require and include are the only mechanisms that allow code in one file to reference code in another. At some point, you need to tell the compiler to load "Install.php".
Autoloading is a way for PHP to ask your code which file it should load, when you mention a class it hasn't seen the definition for yet. The first time a class name is encountered, any function registered with spl_autoload_register will be called with that class name, and then has a chance to run include/require to load the definition. There is a fairly brief overview of autoloading in the PHP manual.
So, in your example:
use Install\Install; just means "when I write Install, I really mean Install\Install"
new Install() is translated by the compiler to new Install\Install()
the class Install\Install hasn't been defined; if an autoload function has been registered, it will be called, with the string "Install\Install" as input
that autoload function can then look at that class name, and run require_once __DIR__ . '/some/path/Install.php';
You can write the autoload function yourself, or you can use an "off-the-shelf" implementation where you just have to configure the directory where your classes are, and then follow a convention for how to name them.
If you want to Use class from another file, you must include or require the file.
Use require('Install.php'); before use Install\Install;.
If you are planning to do a big project I would recommend to use PHP frameworks rather than coding from scratch.

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.

PHP - Class 'My\Engine\Control' not found

The following code is included by another file. My\Engine\Control is defined much earlier and extended all throughout my site with no issues. However in this one file I get the the error:
Fatal error: Class 'My\Engine\Control' not found in
/mnt/web/~/classes.php on line 6
<?php
namespace My\Engine;
// class Control {}
class RequiresAccount extends Control {
public function permissions() {
}
}
Yet when I try to put a dummy Control class in (uncomment the commented part) I get a different error.
Fatal error: Cannot declare class My\Engine\Control, because the name
is already in use in /mnt/web/~/Control.class.php on line 47
It seems impossible and I just can't figure it out. I write code like this all the time and just this one time...
All other files that require extending \My\Engine\Control function perfectly.
It seems that your using some custom autoload logic and some kind of optimizer which puts all the classes together into one file.
I would suggest sticking with PSR-4 standard & Composer library to support and maintain it.
This way your files will be organized and composer will take care of properly handling loading the files and optimizing the loading process for production. All you need to do is to include ./vendor/autoload.php file into your project and define the auto-loading strategy in composer.json file.

Is it allowed to import (via use) a class from a different namespace in PHP when it collides with a class in current namespace?

When using use to import/alias namespace into current namespace in PHP - is it allowed to import to the name of a class that does exist in current namespace but isn‘t used in the current file?
PHP 5.6 and 7.x seem to behave differently here. Unfortunately, I couldn‘t figure out if it‘s allowed at all. If it were forbidden I‘d like my IDE to detect such collisions (because the code might break at any time again), otherwise it‘s a PHP bug in version 5.6.
See minimal code to get the idea:
src/Main/Bar.php:
namespace Cumuru\MinimalExample\Main;
class Bar { }
src/ToBeImported/Bar.php
namespace Cumuru\MinimalExample\ToBeImported;
class Bar { }
src/Main/Foo.php
namespace Cumuru\MinimalExample\Main;
use Cumuru\MinimalExample\ToBeImported\Bar;
class Foo { }
index.php
// [initialize autoloading]
// the following line breaks the code
$bar = new \Cumuru\MinimalExample\Main\Bar();
$foo = new \Cumuru\MinimalExample\Main\Foo();
This code will break in PHP 5.6 while it runs flawlessly in 7.1. Error message is
PHP Fatal error: Cannot use Cumuru\MinimalExample\ToBeImported\Bar as Bar because the name is already in use in .../src/Main/Foo.php on line 4
PHP Stack trace:
PHP 1. {main}() .../index.php:0
PHP 2. spl_autoload_call() .../index.php:6
PHP 3. Composer\Autoload\ClassLoader->loadClass() .../index.php:6
PHP 4. Composer\Autoload\includeFile() .../vendor/composer/ClassLoader.php:322
If you want to actually run the code see this github repo!
I was very lucky to detect the bug before deploying, it could have cost me ages to find it on our production system. I filed a bug report for my IDE but its priority was lowered stating it‘s a
bug in particular version in PHP which is outdated now
I wonder if this is true or if it‘s just an implementation detail of PHP 7 to handle imports on a per-file basis that might be changed in the future (making the code break again).
Edit:
I‘m aware of the possibility to alias class names via use Cumuru\MinimalExample\ToBeImported\Bar as ImportedBar;.
My problem is what happens if class Cumuru\MinimalExample\Main\Bar is created last, with both other classes being unchanged. Class Foo will fatal then - if, and only if, class Cumuru\MinimalExample\Main\Bar has been instantiated before class Foo is loaded (in the same request / process).

PHP namespaces trouble

I just wanna use a namespace in another file, to use the class in it, but im too retarded to do this...
first file:
namespace fun;
use fun\kjr\trouble;
$trouble = new trouble('http://someURL');
second file:
namespace fun\kjr;
class trouble { ... }
This is the error i get:
Error: Fatal error: Class 'fun\kjr\trouble' not found in D:\wamp\www\fun\index.php on line 8
Where did I declare a wrong namespace? Oo
Greetings
I had to include my class aswell.
use namespace
only makes the class available in the actual context.
It does NOT include it, so you can cause it.
use
include_once('your.file')
to do that!
Thanks #Michael Berkowski for helping.
The problem (from what I can see) is you haven't specified an autoloader (e.g. with spl_autoload_register()) or haven't included the file in your code. The use statement will only find a class for you if you have an autoloader in place, otherwise you must include your code ahead of time with include/include_once or require/require once.
I would start with making an autoloader and registering it and then it may be wise to map your namespaces to directory structure as specified by PSR-0.

Categories