PHPUnit signals a warning when mocking a namespace-ed class - php

My composer project consists of the src and tests folders.
The code inside src is auto-loaded using composers psr4 autoloader like so
"autoload": {
"psr-4": {
"christmas\\":"src/"
}
}
The class to be tested looks like so
namespace christmas;
class Hello
{ //constructor & dependency injection included in real class
public function sayHello()
{
return "HELLO";
}
}
And Finally my test class looks like so
<?php
use PHPUnit\Framework\TestCase;
require_once('../vendor/autoload.php');
use christmas\Hello;
class TestHello extends TestCase
{
public function testSayHelloMethod()
{
$hello = $this->getMockBuilder('Hello')
->getMock();
$hello->expects($this->once())
->method('sayHello')
->will($this->returnValue('HELLO'));
$this->assertEquals(
"HELLO",
$hello->sayHello()
);
}
}
And here is how my i run phpunit
phpunit tests/TestHello
phpunit echoes back the following output
Time: 45 ms, Memory: 4.00 MB
There was 1 warning:
1) tests\Hello::testSayHelloMethod()
Trying to configure method "sayHello" which cannot be configured because it does not exist, has not been specified, is final, or is static
/usr/share/php/PHPUnit/TextUI/TestRunner.php:641
/usr/share/php/PHPUnit/TextUI/Command.php:206
/usr/share/php/PHPUnit/TextUI/Command.php:162
WARNINGS!
Tests: 1, Assertions: 0, Warnings: 1.
Below is a view of how my code is organized,
├── composer.json
├── src
│   └── Hello.php
├── tests
│   └── TestHello.php
└── vendor
What am i missing ? I need to have a passing test with no single warning.

You need to pass the full, namespaced name of the class to the mock builder, i.e. 'christmas\Hello'. Since you need to pass it as a string, just using "Hello" isn't enough to properly identify the class.
Since you're already have a use for the class, you can use ::class to get the full name. For example:
$hello = $this->getMockBuilder(Hello::class)
->getMock();

Related

Overriding classes with class_alias

I know this is a really bad idea, but I ran out of ideas, so I'd like to hear some opinions about it.
I'm working on a PHP project which allows the development of addons so that other developers can add their own functions, not so different from thousands of other open-source projects:
root
└── addons
└── addon-a
└── addon-b
└── addon-c
I have to implement some changes to an already made addon (let's say Addon A) without changing the original files or using Composer PSR-4. Unfortunately, the original author did not follow good practices that would help to accomplish this task. Here's how the Addon A is:
Structure
addon-a
└── main.php
└── classes
└── Foo.php
└── Bar.php
addon-a/main.php
include_once __DIR__ . '/classes/Foo.php';
function run() {
echo AddonA\Classes\Foo::returnTwo();
}
addon-a/classes/Foo.php
namespace AddonA\Classes
use AddonA\OtherFolder\FooBar
abstract class Foo {
public static function returnTwo() {
return 2;
}
public static function returnFooBar() {
return new FooBar();
}
}
I need to override the returnTwo function and return another value. Without changing other files, the only way I was able to do it was through the class_alias method, copying the whole Foo class:
First, I created another addon and made it ran before the Addon A "run" function:
Structure
new-addon-a
└── main.php
└── classes
└── Foo.php
new-addon-a/main.php
function init() {
include_once __DIR__ . '/classes/Foo.php';
class_alias( 'NewAddonA\Classes\Foo', 'AddonA\Classes\Foo', true );
}
init();
new-addon-a/classes/Foo.php
namespace NewAddonA\Classes
use AddonA\OtherFolder\FooBar // No changes to the previous "uses."
abstract class Foo {
public static function returnTwo() {
return 3;
}
public static function returnBar() {
return new AddonA\Classes\Bar(); // Here I had to change because the namespace has changed
}
}
Now, when Addon A executes the function run, the AddonA\Classes\Foo::returnTwo will be my own class.
Well, as I said before, this is pretty ugly, and I would lose any update to the Foo code since I'm overriding the entire class. However, this was the only way I thought. Do you think it would be possible to use another method?
Thanks!

How to make sure the correct class file is loaded when calling / extending class

I did brows through that question page but the answers given, I felt, didn't sufficiently answer the question I was struggling with.
I have a little php project. In this project I define two classes both in their own files. The second class extends the first class. In the app.php file I instantiate the second class and call a method.
├── Models/
│ ├── Class1.php
│ └── Class2.php
└── app.php
<?php
// Class1.php
namespace Models\Class1;
/**
* Class1 does things
*/
class Class1 {
public function someMethod() {
// code
}
}
<?php
// Class2.php
namespace Models\Class2;
use \Models\Class1\Class1;
include('./Class1.php');
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
<?php
// app.php
use Models\Class1\Class1;
use Models\Class2\Class2;
require('Models/Class1.php');
require('Models/Class2.php');
$c1 = new Class1();
$c2 = new Class2();
Now I'm not a really experienced php programmer but I thought I had a solid grasp of this class/namespace business and including/requiring files, but apparently I don't.
If I copy the methods I need from Class1 to Class2 and not extend/include/use Class2 everything works fine, but want to extend Class1, so I don't have to repeat myself! Damn it!
This is what I get when running the app.php file.
Warning: include(../Models/Class1.php): failed to open stream: No such file or directory in C:\wamp64\www\project\Models\Class2.php on line 8
Warning: include(): Failed opening '../Models/Class1.php' for inclusion (include_path='.;C:\php\pear') in C:\wamp64\www\project\Models\Class2.php on line 7
I've been reading all I can find online about this stuff, have written and rewritten my code in a dozen different ways but have yet to find a solution. Some help would be greatly appreciated!
Quick Answer
Unless you've specified the full path to the include file, PHP will always try to resolve files according to the location of the entry script. In your case, your entry script seems to be app.php at the root of the application.
So in your Class2.php, instead of include('./Class1.php');, you should write include('Models/Class1.php'); and things should work. But while it fix a short term problem, your code would be really non-portable. Besides, including the same file twice would result in another error (e.g. re-declaring class).
Slightly Smarter Approach
A slightly smarter would be to do this in Class2.php instead.
<?php
// Class2.php
namespace Models\Class2;
use \Models\Class1\Class1;
include_once __DIR__ . '/Class2.php';
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
The variable __DIR__ will always be resolved to the directory of the script file, not the entry script.
But again, it is clumsy and error prone to do file includes by hand.
An Even Smarter Approach: Autoload
PHP supports file autoloading when a class is declare. That is to have a piece of code to do the file include when a class is called. If you want to have fun, you're welcome to write your own autoloader function and register to spl_autoload_register. With a combination of the __DIR__ technique we talked about, you can easily resolve the autoloading path from namespace.
A quick and ugly autoloading app.php would probably look like this:
<?php
// app.php
use Models\Class1\Class1;
use Models\Class2\Class2;
spl_autoload_register(function ($class_name) {
$realClassName = basename(str_replace('\\', DIRECTORY_SEPARATOR, $class_name));
include_once __DIR__ . DIRECTORY_SEPARATOR . 'Models' . DIRECTORY_SEPARATOR . $realClassName . '.php';
});
$c1 = new Class1();
$c2 = new Class2();
The Smart Approach: Composer Autoload
If you're learning OOP PHP today, I'd highly recommend you to learn Composer and PSR-4. PSR-4 defines how you should structure a PHP application for class autoloading. Composer implements a PSR-4 autoloader by default.
First you should comply with the namespace standard. The least change to do is to loose the extra "Class1" and "Class2" in your namespace:
<?php
// Class1.php
namespace Models;
/**
* Class1 does things
*/
class Class1 {
public function someMethod() {
// code
}
}
<?php
// Class2.php
namespace Models;
use \Models\Class1;
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
With a nicely structured application folder, namespace structure and a correctly written autoload.psr-4 section in composer.json, composer will help you to generate a class autoloader. Your composer.json would probably look like this:
{
"autoload": {
"psr-4": {
"Model\\": "Model/"
}
}
}
You may now run composer dump-autoload to create the autoloader. Then you can simply add this to your entry script app.php. When things are ready, you may simply add use and new statement anywhere in the application.:
<?php
// app.php
use Models\Class1;
use Models\Class2;
require_once './vendor/autoload.php';
$c1 = new Class1();
$c2 = new Class2();
All includes are handled by the autoloader. Hands free.
Remove the line
include('./Class1.php');

Composer PSR-4 PHPUnit class not found in same directory

I have the following project structure
src/
├─ MyPackage/
├─ MySDK.php
└─ SDKHelper.php
test/
├─ MySDKTest.php
└─ TestUtils.php
composer.json
composer.lock
My composer.json file looks like this:
"autoload": {
"psr-4": {
"MyPackage\\": "src/MyPackage"
}
}
Everything worked great, and MySDKTest.php unit tests were passing, until I tried adding some utility methods to a third file, TestUtils.php. When I try to call TestUtils::utilityMethod() from MySDKTest.php, phpunit complains that the class TestUtils is not found.
I've been reading about adding an autoload-dev key, variations whereof I have tried, but so far, it appears that nothing is working. I should clarify that I am able to use MySDK and SDKHelper methods inside MySDKTest. MySDKTest.php looks like this when it works:
use MyPackage\MySDK;
class MySDKTest extends PHPUnit_Framework_TestCase {
public function testPackage() {
$sdk = new MySDK();
$sdk->exampleMethod();
}
}
It should be pretty simple. Composer's PSR-4 autoloader just defines a mapping from a namespace to a folder.
Are your tests namespaced correctly? It looks like they're not since you have a use at the top of your test class. If MySDK is in the namepace MyPackage (fully qualified MyPackage\MySDK), I would expect MySDKTest to also be in the MyPackage namespace at MyPackage\MySDKTest. It doesn't have to be that way - you could put the tests in a different namespace if you prefer.
Regardless, the reason it's not working is you didn't register the test folder with the autoloader. The way it looks like your stuff is currently set up, your autoloader config should look like this:
{
"autoload": {
"psr-4": { "MyPackage\\": ["src/MyPackage/", "test/"] }
}
}
Also you'd need to change use MyPackage\MySDK; to namespace MyPackage; in your test class.
Note
Your folder structure is a little weird. I would expect test to match src. So it would be like this:
test/
├─ MyPackage/
├─ MySDK.php
└─ SDKHelper.php
Adjust the namespace accordingly:
{
"autoload": {
"psr-4": { "MyPackage\\": ["src/MyPackage", "test/MyPackage"] }
}
}

Phalcon - Calling function from other controller

How to call function from another controller in Phalcon PHP framework. Here is example for CakePHP http://sherwinrobles.blogspot.com/2013/02/cakephp-calling-function-from-other.html
Based on the link you provided, to my knowledge there is no direct way to call a function in another controller using the request object. However instantiating the controller and calling the function will work just it does in CakePHP
$newController = new \MyNS\Controllers\NewController();
$newController->myFunc();
If you want you can use a static function inside the controller and call it
\MyNS\Controllers\NewController::myFunc();
This is already Tested
For the people that aren't using CakePHP
another way to do this would be to do a helper Folder and write actions, in this case methods.
public/index.php
Add the path helper
$loader->registerDirs(
[
APP_PATH . '/helper/'
]
);
Add a helper Order in apps
└── apps
├── controllers
└─ exampleController.php
├── models
└── helpers
└─ myHelper.php
...
In myHelper.php
<?php
use Phalcon\Di\Injectable;
class myHelper extends Injectable
{
function myNameFunction() {
// here you should write your function
}
}
In exampleController where you want to call the other action, in this case function
<?php
use Phalcon\Mvc\Controller;
class exampleController extends Controller {
public function yourAction() {
//your code
//there are 2 ways of calling the function. either use this one
$myHelper = new myHelper();
$myHelper->myNameFunction();
//or this one
(new UnmatchHelper())->myNameFunction();
}
}

Include and namespace in php

I have created this factory class:
namespace ValidatorFactory;
foreach (glob("Types/*.php") as $filename)
{
include $filename;
}
class ValidatorFactory
{
public static function getValidator($betTypeId)
{
switch ($betTypeId)
{
case 1:
return new B();
break;
default:
return null;
}
}
}
Here is my A abstract class definition:
abstract class A
{
abstract function validateSchema($schema);
}
Here is my B class definition:
class B extends A
{
function validateSchema($schema)
{
return true;
}
}
Now I want to use the factory class in some other file in my project, here is how I am doing it:
$obj = ValidatorFactory::getValidator($someId);
I am using Laravel and via composer (and this tutorial) I tried to load the ValidatorFactory class like built in classes of laravel. Here is what I have added to the composer file:
"autoload": {
"classmap": [
...
],
"psr-0": {
"ValidatorFactory": "app/"
}
},
I have run composer update.
My problem is that my ValidatorFactory is not loaded because I am getting this error:
Class 'ValidatorFactory' not found
If I will add the namespace like this:
$obj = ValidatorFactory\ValidatorFactory::getValidator($someId);
I am getting other error, here it is:
Class 'ValidatorFactory\B' not found
So there are 2 problems, first related to larave/composer autoload (of the namespace). The second is related to includes and inheritance (as I suspect) in php.
How can I solve this two issues? Thanks!
Update:
As suggested in the answer, I have added the same namespace for all 3 involved classes A, B and the factory. No class including any other class.
In the class which calls the factory's getValidator function I am doing it like this:
$obj = ValidatorFactory\ValidatorFactory::getValidator($someId);
Still getting the error which says class B is unknown :(
Here is how the updated class B looks like:
namespace ValidatorFactory;
class B extends A
{
function validateSchema($schema)
{
return true;
}
}
The file structure looks like this: factory file located in app/dir1/ , Class A and B located in app/dir1/dir2/
Anything defined in an include is defined in the global namespace, so the abstract class definitions define the \A and \B classes, respectively (and not ValidatorFactory\A or ValidatorFactory\B). In order to use them the way you are, you would need to add a use statement for each of them after they are included.
But why include anything at all? This goes agains the very idea of an autoloader. If you want to declare a class, put it in one of your namespaces and let Laravel and composer do the hard work for you.
Update
return new B();
Try changing that line to:
return new \B();
And that will clear up your second error. As for the first, you declared your Validator factory to live in a namespace:
namespace ValidatorFactory;
Any code that uses that class (which is not in the same namespace) will need to either A) import the class with a use statement, or B) use the Fully-Qualified Namespace whenever referencing it:
use ValidatorFactory\ValidatorFactory;
// ...
$factory = new ValidatorFactory();
Or
$factory = new ValidatorFactory\ValidatorFactory();
Remember, all namespaces are for is to allow you and another developer to name a class exactly the same thing. For instance, perhaps I'm working on some sort of route mapping tool. I may want to define a Route class, because that name fits extremely well with my current problem domain. However, Taylor already beat me to the punch because there already is a Route class. Using a name space I can ensure that I can still name anything I want whatever I want however I want to and there won't be any conflicts with stuff that has already been named or stuff that has yet to be named.
Update 2
Namespaces in projects using composer need to mirror the directory path in which the underlying files are located (this is how the autoloader can find them- it basically looks at the namespace and classname and turns that into a directory path to the file, and appends a .php extension). If you have classes like this:
// File: app/dir1/dir2/A.php
class A{}
// File: app/dir1/dir3/B.php
class B{}
Then A will have to be in the namespace dir1\dir2, and its fully qualified name would be dir1\dir2\A. Similarly, class B would have to be in the namespace dir1\dir3 and its fully qualified name would be dir1\dir3\B.
From our chat discussion, we couldn't figure out why PSR-0 is not working for you. It turns out that PSR-4 configuration works fine. So I summarise here what we did in the discussion to use PSR-4 autoloading.
File structure:
app
|- commands
|- config
|- ...
|- MyValidator
|- MyValidatorFactory.php
|- A.php
|- B.php
|- ...
MyValidatorFactory.php
namespace MyValidator;
class MyValidatorFactory
{
...
return new B();
...
}
B.php
namespace MyValidator;
class B extends A
{
function validateSchema($schema)
{
return true;
}
}
And finally you can setup PSR-4 autoloading in your composer.json:
"autoload": {
...
"psr-4": {
"MyValidator\\": "app/MyValidator"
}
}
Bonus point:
With the psr-4 configuration above, you may also structure your namespaces and classes like this:
app
|- commands
|- config
|- ...
|- MyApp
|- Validators
|- MyValidatorFactory.php
|- A.php
|- B.php
|- ...
And set the psr-4 config to "MyApp\\": "app/MyApp", the autoloader will recognise your classes and use it like below:
new \MyApp\Validators\MyValidatorFactory;
new \MyApp\Validators\A;
new \MyApp\Validators\B;

Categories