trying to use a trait but keeps saying not found - php

I have a api trait that connects to an external endpoint. I want to use this trait in a class called ProductClass. The trait is in the same folder as the class, but I get a error is I add use ApiTrait in the class. Error says it cannot find the trait, So if I include the trait file at the top of the class file, I get this error, cannot find ApiTrait in
ProductClass\ApiTrait.
If i pass the trait into the constructor I get an error from my index page when I call the ProductClass because I am not passing in the trait. I dont want to pass any params to the constructor just the string top append to the .env endpoint. any clues greatly appreciated
heres my ApiTrait code
<?php
namespace ApiTrait;
require './vendor/autoload.php';
use GuzzleHttp\Client;
trait ApiTrait
{
protected $url;
protected $client;
public function __construct()
{
$this->url = getenv('API_URL');
$this->client = new Client();
}
private function getResponse(String $uri = null)
{
$full_path = $this->url;
$full_path .=$uri;
try {
$response = $this->client->get($full_path);
}
catch (GuzzleHttp\Exception\ClientException $e) {
$response = $e->getResponse();
}
return json_decode($response->getBody()->getContents(), true);
}
public function getAPIData($uri)
{
return $this->getResponse($uri);
}
}
this is my ProductClass code
<?php
namespace ProductClass;
include_once("ApiTrait.php");
use DataInterface\DataInterface;
class Product implements DataInterface
{
use ApiTrait\ApiTrait
private $api;
public function __construct(ApiTrait\ApiTrait $apiTrait) {
$this->api = $apiTrait;
}
private function getResponse($append, $try) {
$urlAppend = $append;
$good_data = false;
do{
try{
$result = $this->api->getAPIData($urlAppend);
//check data to see if valid
if(!array_key_exists( "error",$result)){
$good_data = true;
return $result;
}
}
catch(Exception $e){
//call api upto 10 times
if($try < 10) {
sleep(1);
getData($append, $try++);
} else { //return a connection error
$api_error['error']='unable to connect to api';
return $api_error;
}
}
} while($good_data === false);
}
public function getData($append, $try = 0)
{
return $this->getResponse($append, $try);
}
}

If you're using an autloader, you shouldn't ever need this:
include_once("ApiTrait.php");
You've got your trait defined in the ApiTrait namespace:
namespace ApiTrait;
trait ApiTrait { ... }
I.e., the trait's full path is \ApiTrait\ApiTrait. If you're using the trait in a namespace other than the one it's defined, then you need to anchor from the root namespace when referring to it, by preceding it with a backslash:
namespace ProductClass;
class Product implements DataInterface
{
use \ApiTrait\ApiTrait;
Otherwise, if you do use ApiTrait\ApiTrait; without the leading backslash, then PHP thinks you're referring to the current namespace, which is ProductClass, yielding \ProductClass\ApiTrait\ApiTrait -- which doesn't exist, hence your error.
You could also do it this way with class aliases:
namespace ProductClass;
use ApiTrait\ApiTrait;
class Product implements DataInterface
{
use ApiTrait;
Also, it looks like you're just putting every class it its own namespace. Don't do that. Use namespaces to group common items, for example, something like this:
namespace Traits;
trait Api { ... }
namespace Traits;
trait Foo { ... }
namespace Traits;
trait Bar { ... }
namespace App;
class Product {
use \Traits\Api;
use \Traits\Foo;
use \Traits\Bar;
}

Related

makePartial() returns Mockery\Exception\BadMethodCallException : Method does not exist on this mock object

I'm trying to test my Category class. I'm using Mockery::mock() method, with 'overload:' prefix and makePartial() method.
When running test I have this error:
Mockery\Exception\BadMethodCallException : Method App\Models\Category::getDynamicFieldsForDocument() does not exist on this mock object
Here is my code:
namespace App\Models;
class Category extends DictionaryBase{
//some methods
public function getDynamicFieldsForDocument()
{
$data = [];
$parents = [];
$allParents = $this->getParents($this->id, $parents);
foreach ($allParents as $parentId) {
$parent = Category::find($parentId);
$fields = $parent->dynamicFields();
foreach ($fields as $field) {
$data[$field['name']] = $field;
}
}
return $data;
}
}
TestCase:
namespace Tests\Unit;
use App\Models\Category;
use Tests\TestCase;
class CategoryModelTest extends TestCase{
//some methods
/**
* #runInSeparateProcess
* #preserveGlobalState disabled
*/
public function testGetDynamicFieldsForDocument()
{
$mockCategory = \Mockery::mock('overload:'.Category::class)->makePartial();
$preparedDynamicFields = $this->prepareDynamicFields();
$preparedCategories = $this->prepareCategories();
$mockCategory->shouldReceive('find')->andReturn($preparedCategories[0], $preparedCategories[1], $preparedCategories[2]);
$mockCategory->shouldReceive('getParents')->andReturn(['1a2b', '3c4d', '5e6f']);
$mockCategory->shouldReceive('dynamicFields')->andReturn(null, $preparedDynamicFields[0], $preparedDynamicFields[1]);
$response = $mockCategory->getDynamicFieldsForDocument();
dd($response);
}
}
I have no idea why i still have error. I think when ->makePartial() method is called it should mock only methods, which are called by ->shouldReceive()
EDIT:
Now I'm making mock instance without :overload, and mocking 'find' method in this way:
`$mockCategory->shouldReceive('find')->andReturn($preparedCategories[0], $preparedCategories[1], $preparedCategories[2]);`
My find method looks like this:
public static function find($id) {
return $id ? self::list(config(static::IDENT.'.fields'), (new Filter('and'))->add('id', $id, '')->toArray(),[],1,1)[0] ?? null : null;
}
And this is my error:
Error : Wrong parameters for App\Exceptions\ApiException([string
$message [, long $code [, Throwable $previous = NULL]]])
It's because list method call API so it looks like this method is called without mock.
I know that i can't mock static method, but earlier when I used :overload it was possible. What's now?
Delete :overload and just define your mock as:
$mockCategory = \Mockery::mock(Category::class)->makePartial()
Example
Model:
namespace App\Models;
class Foobar extends BaseModel
{
public function foonction()
{
Foobar::find();
return '1';
}
}
Test:
namespace Tests;
use Evalua\Heva\Models\Foobar;
class FoobarTest extends TestCase
{
public function testFoobar()
{
$fooMock = \Mockery::mock('overload:'.Foobar::class)->makePartial();
$fooMock->shouldReceive('find')->once();
$fooMock->foonction();
}
}
Fails with:
Mockery\Exception\BadMethodCallException: Method Evalua\Heva\Models\Foobar::foonction() does not exist on this mock object
Without the :overload the test pass.
The explanation should be based on what's written in the documentation about overload:
Prefixing the valid name of a class (which is NOT currently loaded) with “overload:” will generate an alias mock (as with “alias:”) except that created new instances of that class will import any expectations set on the origin mock ($mock). The origin mock is never verified since it’s used an expectation store for new instances. For this purpose we use the term “instance mock”

PHP won't catch error inside class, only on caller

I'm trying to catch an error from my JWT class but i can't do it inside the class, the only place i can get it is from my main caller.
I'm calling this class with the error from my "API" where i start with the routing:
$router = new Router();
$router->all('/users', function()
{
$controller = new Controllers\UserController();
$controller->start();
});
$router->run();
After that i have my controller that will call my "API" class:
class UserAPI extends BaseAPI
{
protected $user;
protected $apiBase = "user";
function __construct($request, $origin)
{
parent::__construct($request);
$this->user = new User();
}
protected function logout()
{
if( isset($this->request[$this->apiBase . 'Data']) )
{
return $this->usuario->login($this->request[$this->apiBase . 'Data']);
}
else
{
return Helpers::errorResponse("User data not informed", 200);
}
}
}
And finally i have the problem, the User class where i want to catch an error but it wont work:
class User extends SimpleModel
{
public function logout($userData)
{
try
{
//At this point i will get an error since the provided token is invalid on purpose
$jwt = JWT::decode($userData['token'], SECRET_KEY, ['HS512']);
}
//Wont hit here even for a miracle
catch (Exception $exception)
{
echo "Caught ExceptFoo\n";
echo "Message: {$exception->getMessage()}\n";
}
}
}
The only place i could catch this error was on the routing file, wich is my index.php file.
For the JWT class i'm using Firebase JWT.
Relative class names (like Exception in your example) are always rooted to the namespace you are within. If you don't define a namespace, \ is used. Consider:
<?php
namespace Foo;
use Vendor\Package\Bar;
try {
Bar::throwAnException();
} catch (Exception $ex) {
die((string)$ex);
}
Here we have two relative class paths: Bar and Exception. PHP resolves Bar via the use statement to the absolute class path \Vendor\Package\Bar. PHP doesn't have a use statement corresponding to Exception, so PHP assumes you mean \Foo\Exception.
Clearly this isn't your intent. Unfortunately, PHP is silent when this situation occurs. It's bitten me a few times.

Unit testing the instance from a factory

I'm trying to create a unit test for a Factory class,
but PHPUnit is returning me a fatal error on the creation of the Product.
Fatal error: Class 'JsonStorage' not found
Files structure
-\
-libs
-My
-Storage
-IStorage.php
-IStorageFactory.php
-StorageFactory.php
-JsonStorage.php
-FileStorage.php
-tests
-StorageFactoryTest.php
This is the
Creator
use \tests;
namespace My\Storage;
class StorageFactory implements IStorageFactory
{
public static function build($type)
{
$classname = "Storage";
if (is_string($type)) {
switch($type = ucfirst(strtolower(trim($type)))) {
case "Json": // Fall-through
case "File":
$classname = $type . $classname;
break;
default:
throw new \Exception("Storage type not recognized or not yet implemented: please provide a valid type");
}
if (class_exists($classname)) {
try {
return new $classname;
} catch (Exception $e) {
throw new \Exception("Cannot create Storage object: please provide a valid type");
}
} else {
throw new \Exception("Class not recognized");
}
Product of the factory
can be
namespace My\Storage;
class FileStorage implements IStorage
{
private $filepath;
private $data;
private $handle = null;
public function __construct($filepath = null)
{
if (isset($filepath)) {
$this->setPath($filepath);
}
}
//....
}
or (the case I'm testing)
namespace My\Storage;
class JsonStorage extends FileStorage
{
// .... additional implementations
}
and I have a
Testing class
use \tests;
namespace My\Form\Storage;
require_once _DIR_ . "\..\My\Form\Storage\IStorage.php";
require_once .... <all the other classes>.....
require_once
class StorageFactoryTest extends \PHPUnit_Framework_TestCase
{
public function testCanBuildJSON()
{
$s = StorageFactory::build("JSON");
$this->assertInstanceOf($s, "IStorage"); // Or better directly 'JsonStorage'?
}
}
Maybe is something wrong with the namespaces, but I cannot understand why the JsonStorage class is not found.
The solution was easy (but took me a lot..)
I checked that the class was declared with get_declared_classes().
Then I read that assertInstanceOf needs a full qualified namespace if executed inside a namespace.
So I just changed the
Creator
$classname = __NAMESPACE__."\\".$classname;
if (class_exists($classname)) {
try {
return new $classname;
} //...
and the
Test
public function testCanBuildJSON()
{
$s = StorageFactory::build("JSON");
$this->assertInstanceOf(__NAMESPACE__."\\JsonStorage", $s);
}
Hope it could help someone.
PS - Feel free to add details or suggestions for better practices

using same namespace php, Call to undefined function

using same namespace php
I have this files in the same folder :
OtherFunctions.php
<?php
namespace Pack\sp;
$Tble = NULL;
function SetTble($tble) {
global $Tble;
$Tble = $tble;
}
function GetTble() {
global $Tble;
return $Tble;
}
function Funct0($Str0, $Str1) {
return $Str0 == $Str1;
}
function Funct1($Arg) {
return "The Value is ".$Arg;
}
//... from 0 to 16
function Funct16($Arg) {
return "The Value is ".$Arg;
}
?>
How to call all functions contained in this file?
In one class File SubClass.php I have this:
<?php
namespace Pack\sp;
class SubClass {
public $CArg = "";
}
?>
In other class File LeadClass.php
I have this:
<?php
namespace Pack\sp;
use \Pack\sp\SubClass;
require_once("OtherFunctions.php");
class LeadClass {
public function __construct($Name) {
echo("_._");
$NewSC = new SubClass();
$NewSC->CArg = $Name;
SetTble($Name);
echo("ini:".GetTble().":end");
}
}
?>
I want call all function in one instruction of OtherFunctions.php File, but I don't kno how to do it....
I trying to replicate this message in other code
Fatal error: Call to undefined function GetTble() in C:...\LeadClass.php on line 10
But, I'm obtaining blank page
EDIT
Was added the line:
require_once("OtherFunctions.php");
And was replaced the line:
require_once("SubClass.php");
by the line:
use \Pack\sp\SubClass;
in LeadClass.php File.
But, I'm obtaining blank page
You need to add the next line
namespace Pack\sp;
use \Pack\sp\SubClass; // <--- add this
Also I think you should put the functios of the OtherFunctions file into a new class link
namespace Pack\sp;
class OtherFunctions{
// your current code goes here
}
After that you need to extend the SubClass whit the OtherFunctios class
namespace Pack\sp;
use Pack\sp\OtherFunctions;
class SubClass extends OtherFunctions {
public $CArg = "";
}
EDIT
I just tried your code and I can make the LeasClass to work as follow
<?php
namespace Pack\sp;
require_once("OtherFunctions.php");
require_once("SubClass.php");
class LeadClass {
public function __construct($Name) {
echo("_._");
$NewSC = new SubClass();
$NewSC->CArg = $Name;
SetTble($Name);
echo("ini:".GetTble().":end");
}
}
$LeadClass = new LeadClass('table');
?>
Have you already initialize the class?

Laravel dependency injection "not instantiable"

I have spent a day trying debugging and trying to understand what is going wrong but...
So here is my code:
<?php
namespace RememberCalories\Rest;
interface MyJsonResponseInterface
{
public function getResponse();
}
And here is the class which I want to inject:
<?php
namespace RememberCalories\Rest;
class MyJsonResponse implements MyJsonResponseInterface
{
protected $success;
protected $responseCode;
protected $responseMsg;
protected $data;
public function __construct($data, $responseCode=0, $responseMsg='')
{
$this->data = $data;
$this->responseCode = $responseCode;
$this->responseMsg = $responseMsg;
if ( $this->responseCode === 0 ) {
$this->success = true;
}
}
...
Binding:
\App::bind('MyJsonResponseInterface', function($app, $parameters) {
$obj = new \RememberCalories\Rest\MyJsonResponse(null);
// var_dump($obj);
// die();
return $obj;
});
And at last the controller:
<?php
use \RememberCalories\MainMenu\MainMenu;
use \RememberCalories\Repository\TargetEloquentRepository as TargetRepository;
use \RememberCalories\Rest\MyJsonResponseInterface;
//use \RememberCalories\Rest\MyJsonResponse;
class BaseController extends Controller
{
protected $viewVars;
protected $mainMenu;
//Dependency injection classes
protected $target;
protected $myJsonResponse;
public function __construct(TargetRepository $target, MyJsonResponseInterface $myJsonResponse )
{
$this->beforeFilter('accessFilter');
$this->target = $target;
//$this->myJsonResponse = $myJsonResponse;
$this->mainMenu = (new MainMenu())->build();
$this->prepareViewVariables();
}
So the problem is with the second parameter of BaseController: MyJsonResponseInterface. The first is injected without problems but this one I get an error:
Illuminate \ Container \ BindingResolutionException
Target [RememberCalories\Rest\MyJsonResponseInterface] is not
instantiable.
It seems that the Closure in \App::bind('MyJsonResponseInterface' ...) is not called.
I have moved it to service provider with the same result.
But at the same if to call manually \App::make('MyJsonResponseInterface') everything is created ideally.
Please advise what way to investigate.
You need to App::bind the full namespace - so in your example, App::bind('RememberCalories\Rest\MyJsonResponseInterface').

Categories