PHP - How can I test this class? - php

I'm just getting started with PHPUnit and I'm struggling on how to test certain features. For example, I've got the following class with loads the DotEnv library, and I'd like to test the following features...
Test the variables are loaded
Test it doesn't if the config is already cached
Test it throws an exception if a required variable is missing
But I'm struggling with the best way to do this $app->configurationIsCached() is managed elsewhere so blocks then rest of the class from executing.
<?php declare(strict_types=1);
namespace Foundation\Bootstrap;
use Dotenv\Dotenv;
use Foundation\Core;
class LoadEnvironmentVariables
{
/**
* Any required variables.
*
* #var array
*/
protected $required = [
'APP_URL',
'DB_NAME',
'DB_USER',
'DB_PASS',
'DB_HOST'
];
/**
* Creates a new instance.
*
* #param Core $app The application instance.
*/
public function __construct(Core $app)
{
// If the configuration is cached, then we don't need DotEnv.
if ($app->configurationIsCached()) {
return;
}
// Load the DotEnv instance
$this->load($app->get('paths.base'));
}
/**
* Loads the .env file at the given path
*
* #param string $filePath The path to the .env file
* #return void
*/
public function load(string $filePath)
{
$dotEnv = Dotenv::create($filePath);
$dotEnv->safeLoad();
$dotEnv->required($this->required);
}
}

Regarding your code getting tied up on the $app->configurationIsCached():
Use something like Mockery to create a mock of your Core class you're passing in as $app to your class. You can then mock configurationIsCached(), having it return whatever is needed to route your class through to the early return or the call to your load() method.

Related

I wonder how this code works: it is related to classes in PHP

I am working with a framework to build a telegram bot, since I am not a pro in PHP, I am trying to understand the logic behind the codes; there is one thing I cannot understand. then, I decided to ask it here.
The code is much bigger; so I cut some parts of it. I have a specific problem with this method. I don't exactly get how they specified a method for getMessage(). It didn't have any functions with this name in this class.
<?php
/**
* Class Command
*
* Base class for commands. It includes some helper methods that can fetch data directly from the Update object.
*
* #method Message getMessage() Optional. New incoming message of any kind — text, photo, sticker, etc.
abstract class Command
{
/**
* Constructor
*
* #param Telegram $telegram
* #param Update|null $update
*/
public function __construct(Telegram $telegram, ?Update $update = null)
{
$this->telegram = $telegram;
if ($update !== null) {
$this->setUpdate($update);
}
$this->config = $telegram->getCommandConfig($this->name);
}
/**
* Set update object
*
* #param Update $update
*
* #return Command
*/
public function setUpdate(Update $update): Command
{
$this->update = $update;
return $this;
}
/**
* Pre-execute command
*
* #return ServerResponse
* #throws TelegramException
*/
public function preExecute(): ServerResponse
{
if ($this->need_mysql && !($this->telegram->isDbEnabled() && DB::isDbConnected())) {
return $this->executeNoDb();
}
if ($this->isPrivateOnly() && $this->removeNonPrivateMessage()) {
$message = $this->getMessage();
if ($user = $message->getFrom()) {
return Request::sendMessage([
'chat_id' => $user->getId(),
'parse_mode' => 'Markdown',
'text' => sprintf(
"/%s command is only available in a private chat.\n(`%s`)",
$this->getName(),
$message->getText()
),
]);
}
return Request::emptyResponse();
}
I just don't get it how this->getMessage() is working. Is there somebody help me out?
This is a base class, and some other class is extending this class. Since this is an abstract class, it cannot be instantiated.
So you need to look for another class that extends this class (i.e class SomeCommand extends Command). The SomeCommand class in this case will declare the getMessage function.
(Note that SomeCommand is an example, I don't know the real name of the other class.)
Edit:
That function was removed in this commit in favor of using the magic method __call instead. No idea why.

Laravel class Auth

Hi can I ask about this in laravel framework
namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Auth\AuthManager
* #see \Illuminate\Contracts\Auth\Factory
* #see \Illuminate\Contracts\Auth\Guard
* #see \Illuminate\Contracts\Auth\StatefulGuard
*/
class Auth extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'auth';
}
}
what does the return 'auth' exactly returning to the caller ? is it text 'auth' or an object ? and what is the reason why they only have one method in that class ? I apologize i am just learning oop.
Thank you in advance.
In this case as you see method getFacadeAccessor it's returning auth string.
Facades are just "shortcuts" to use other classes but in fact you shouldn't use them everywhere if you don't need to.
In Laravel you can bind objects/classes into Application. So you can write for example:
$app->bind('something', function() {
return new SomeObject();
});
Let's assume there is method doSomething in SomeObject class.
Now you can use this method using:
$app['something']->doSomething();
But you can also create facade:
class GreatClass extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'something';
}
}
and now anywhere in your application you could use:
GreatClass::doSomething();
Answering your question this getFacadeAccessor is returning only the name the name of object that is used when bound to Application. To know how it's used you can look into the source of:
/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
The method you should look first is getFacadeRoot - because this method is returning the requested object.

Searching for Schema:table static method definition in Laravel Framework

I'm trying to understand PHP Laravel Framework. Whend dealing with databases, we use Schema::table to create a table in the database. Searching in my application I find the only definition is
<?php namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Database\Schema\Builder
*/
class Schema extends Facade {
/**
* Get a schema builder instance for a connection.
*
* #param string $name
* #return \Illuminate\Database\Schema\Builder
*/
public static function connection($name)
{
return static::$app['db']->connection($name)->getSchemaBuilder();
}
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return static::$app['db']->connection()->getSchemaBuilder();
}
}
but there is not a table static method neither in Schema class or in Facade class.
What piece I'm missing?
Schema here is only facade. Method table() and other methods from Schema can be found in file: \vendor\laravel\framework\src\Illuminate\Database\Schema\Builder.php .
Other methods can be found in \vendor\laravel\framework\src\Illuminate\Database\Schema\Blueprint.php
You should always look at Facade Class Reference to know what class you are really using.

Can one modify the templates created by artisan migrate command?

I've created a base class for my migrations. At the moment I run the artisan migrate command and it creates a new migration that extends the Migrations file, however I want to include my BaseMigration and extend it from there. I've been making this changes manualy but I feel like I'm repeating myself unnecessarily.
Any advice on how to have new migrations automatically extend and load my base migration?
It's doable in a fairly logical way, at least in Laravel 5
Subclass MigrationCreator and override getStubPath(), just copying the function over from the original class (it will use your subclass's __DIR__)
<?php
namespace App\Database;
use Illuminate\Database\Migrations\MigrationCreator;
class AppMigrationCreator extends MigrationCreator
{
public function getStubPath()
{
return __DIR__.'/stubs';
}
}
Write a service provider to override migration.creator with your own subclass (it must be a deferred service provider, because you cannot override a deferred binding with an eager one):
<?php
namespace App\Database;
use Illuminate\Support\ServiceProvider;
class AppMigrationServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->singleton('migration.creator', function ($app) {
return new AppMigrationCreator($app['files']);
});
}
public function provides()
{
return ['migration.creator'];
}
}
Add your service provider to config/app.php after the default ones.
Finally, copy vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs alongside your MigrationCreator subclass (in this example it would become app/Database/stubs) and edit the templates to your needs.
Keep the DummyClass and DummyTable names, as they are replaced with str_replace() to create the actual migrations files.
Since Laravel 7 you can publish stubs using php artisan stub:publish.
The published stubs will be located within a stubs directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan make commands.
I don't think you can, because Laravel takes migrations from the vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs folder and you cannot change that, but you have some options:
1) Create your own artisan command migrate:makemyown.
2) Use Jeffrey Way's Laravel Generators. They let you create your migrations by doing:
php artisan generate:migration create_posts_table --fields="title:string, description:text"
If you just have some fields you need to start with and not something more specific than that, it works really fine.
3) Edit Laravel stubs, but the problem is that as soon as you composer update they might get overwritten by Composer.
I believe that there is no way to override this (for now) but I think that you can create your custom command which will use Laravel logic. This was created for Laravel 5.
First you have to create Generator command app/Console/Commands/Generator.php:
<?php namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Input\InputArgument;
class Generator extends Command
{
/**
* Command name
*
* #var string
*/
protected $name = 'generate';
/**
* Command description
*
* #var string
*/
protected $description = 'Custom object generator';
/**
* An array with all available generator classes
*
* #var array
*/
protected $types = ['request', 'model', 'middleware'];
/**
* Execute command
*
* #return mixed
*/
public function handle()
{
$type = $this->argument('type');
if (!in_array($type, $this->types)) {
return $this->error('Type must be one of: '.implode(', ', $this->types));
}
// Create new instance
$generatorClass = 'App\Console\Commands\Generators\\'.ucfirst($type);
$generator = new $generatorClass(new Filesystem());
// Each generator has "fire" method
$this->comment($generator->setClassName($this->argument('name'))->fire());
}
/**
* #return array
*/
public function getArguments()
{
return [
['type', InputArgument::REQUIRED, 'Type of class to generate: '.implode(', ', $this->types)],
['name', InputArgument::REQUIRED, 'Name of class to generate'],
];
}
}
Then you have to create an abstract class for all of your Generators classes app/Console/Commands/Generators/Generator.php:
<?php namespace App\Console\Commands\Generators;
use Illuminate\Console\GeneratorCommand;
abstract class Generator extends GeneratorCommand
{
// Directory name with whole application (by default app)
const APP_PATH = 'app';
/*
* Name and description of command wont be used
* Generators Commands are not loaded via Kernel
* Name and description property has been put just to avoid Exception thrown by Symfony Command class
*/
protected $name = 'fake';
protected $description = 'fake';
/**
* Class name to generate
*
* #var string
*/
protected $className;
/**
* Returns class name to generate
*
* #return string
*/
protected function getNameInput()
{
return $this->className;
}
/**
* Returns path under which class should be generated
*
* #param string $name
* #return string
*/
protected function getPath($name)
{
$name = str_replace($this->getAppNamespace(), '', $name);
return self::APP_PATH.'/'.str_replace('\\', '/', $name).'.php';
}
/**
* Sets class name to generate
*
* #param string $name
* #return $this
*/
public function setClassName($name)
{
$this->className = $name;
return $this;
}
/**
* Execute command
*
* #return string
*/
public function fire()
{
$name = $this->parseName($this->getNameInput());
if ($this->files->exists($path = $this->getPath($name)))
{
return $this->type.' already exists!';
}
$this->makeDirectory($path);
$this->files->put($path, $this->buildClass($name));
return $this->type.' '.$this->className.' created successfully.';
}
}
At the end you can create your first Generator class! app/Console/Commands/Generators/Request.php
<?php namespace App\Console\Commands\Generators;
class Request extends Generator
{
/**
* Class type to generate
*
* #var string
*/
protected $type = 'Request';
/**
* Returns default namespace for objects being generated
*
* #param string $rootNamespace
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Http\Requests';
}
/**
* Returns path to custom stub
*
* #return string
*/
public function getStub()
{
return base_path('resources').'/stubs/request.stub';
}
}
Dont forget to add your generate command to Kernel app/Console/Kernel.php:
<?php namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel {
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
...
'App\Console\Commands\Generator',
...
];
Put your stubs under resources/stubs directory. Let's create first one for Request Generator resources/stubs/request.stub:
<?php namespace {{namespace}};
class {{class}} extends Request
{
/**
* #return bool
*/
public function authorize()
{
// CUSTOM LOGIC
return false;
}
/**
* #return array
*/
public function rules()
{
$rules = [];
// CUSTOM LOGIC
return $rules;
}
}
Then call with php artisan generate request MyRequest.
You can create your custom Model, Middleware, Controller etc. generators, it's very simple - you have to create new generator class under app/Commands/Console/Generators - take a look at Request.php generator to see how it works!
For Laravel 5 you'd edit one of the .stub files in:
vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs
There's no reason why you can't edit those files.
Search in vendor/laravel/framework/src/ for .stub files to find all of the other stubs (templates) artisan uses.

Namespaces in PHP preventing using of Yii static functions

I just implemented namespaces in my small application as outlined here: http://www.yiiframework.com/doc/guide/1.1/en/basics.namespace
I'm running into an issue where my controller will no longer access Yii::app()->getRequest(); saying it can't find include(C:\Users\bkuhl\htdocs\instaLabel\application\protected\components\Yii.php): failed to open stream: No such file or directory.
I realize that's because I declared the namespace as application/components. But I'm not sure how to work around this one...
<?php
namespace application\components;
/**
* Controller is the customized base controller class.
* All controller classes for this application should extend from this base class.
*/
class Controller extends \CController {
/* #var $request CHttpRequest */
protected $request = null;
/**
* #var string the default layout for the controller view. Defaults to '//layouts/column1',
* meaning using a single column layout. See 'protected/views/layouts/column1.php'.
*/
public $layout='//layouts/column1';
/**
* #var array context menu items. This property will be assigned to {#link CMenu::items}.
*/
public $menu=array();
/**
* #var array the breadcrumbs of the current page. The value of this property will
* be assigned to {#link CBreadcrumbs::links}. Please refer to {#link CBreadcrumbs::links}
* for more details on how to specify this property.
*/
public $breadcrumbs=array();
public function __construct ($id, $module = null) {
parent::__construct($id, $module);
$this->request = Yii::app()->getRequest();
}
You need to fully qualify the relative class name Yii.
The most convenient way to do this is by importing the class: just add use Yii; below your namespace declaration.
Have you tried:
$this->request = \Yii::app()->getRequest();
\ will use the global namespace:
Prefixing a name with \ will specify that the name is required from the global space even in the context of the namespace.

Categories