I'm relatively new to PHPUnit and TDD, and I was wondering how I might test the following code:
class File
{
/**
* Write data to a given file
*
* #param string $file
* #param string $content
* #return mixed
*/
public function put($path, $content)
{
return file_put_contents($path, $content);
}
}
How can I test if the file was created WITHOUT actually creating the file (with PHPUnit obviously).
Thanks.
You can mock the file system for your unit tests by using a virtual file system like vfsStream with documentation here
EDIT
An example would be something like:
class FileTest extends \PHPUnit_Framework_TestCase
{
/**
* #var vfsStreamDirectory
*/
private $root;
/**
* set up test environmemt
*/
public function setUp()
{
$this->root = vfsStream::setup('exampleDir');
}
/**
* test that the file is created
*/
public function testFileIsCreated()
{
$example = new File();
$filename = 'hello.txt';
$content = 'Hello world';
$this->assertFalse($this->root->hasChild($filename));
$example->put(vfsStream::url('exampleDir/' . $filename), $content);
$this->assertTrue($this->root->hasChild($filename));
}
}
Related
I have an artisan command which gets some options, one of these options is --type=, like below:
protected $signature = 'make:procedure {name} {--type=}';
--type= contains the kind of difference, I want to check this option in the stub because each type has a different namespace which should be used in the stub.
for example, this is my stub:
<?php
namespace DummyNamespace;
class DummyClass
{
//
}
How can I do this, (of course this is an example, I just trying to explain my problem):
<?php
namespace DummyNamespace;
if ($type === 'one') {
echo 'use App\Some\Namespace\One'
}
class DummyClass
{
//
}
It would be highly appreciated if anyone can advise me!😊
your Custom command should derive from GeneratorCommand then you can use abstract Method getStub()
Your Stub File
namespace DummyNamespace;
/**
* Class DummyClass.
*/
class DummyClass
{
}
In Your Command File, you just need to use below code
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path('file/path/test.stub');
}
For Explanation Only
In GeneratorCommand class
/**
* Get the stub file for the generator.
*
* #return string
*/
abstract protected function getStub();
/**
* Build the class with the given name.
*
* #param string $name
* #return string
*
* #throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function buildClass($name)
{
$stub = $this->files->get($this->getStub());
return $this->replaceNamespace($stub, $name)->replaceClass($stub, $name);
}
A very simple way to generate files from stub.
In your stub file
namespace {{namespace}};
/**
* Class {{name}}.
*/
class {{name}}
{
}
Somewhere in your command
protected function getStub()
{
return file_get_contents(resource_path('stubs/dummy.stub'));
}
protected function generate($namespace, $name)
{
$template = str_replace(
['{{namespace}}', '{{name}}'],
[$namespace, $name],
$this->getStub()
);
file_put_contents(app_path("Dummies/$name.php"), $template);
}
I try to learn oop but in my first class it gives me this error.
Database class
<?php
namespace App;
class Database
{
...
}
in my functions.php
<?php
require 'helpers.php';
require 'connection.php';
use App\Database;
...
Class under the "app" folder and it's namespace is "App". Why I'm getting this error ?
You either need to include the file, or use an AutoLoader. AutoLoaders tell PHP where a class can be found, as PHP needs to know the file.
Autoloaders are fully explained in the PHP documentation:
https://secure.php.net/manual/en/language.oop5.autoload.php
Example from the mentioned documentation:
<?php
spl_autoload_register(function ($class_name) {
include $class_name . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
In this case spl_autoload_register is used to register the autoloader. The autoloader is a function which takes the class name, and includes the necessary class. For example you can use the autoloader function as used above, in which case the class name needs to be identical to the filename.
This is a quite simple example, but a more advanced autoloader could check if files exist, check multiple locations, etc...
Examples are mentioned in the comments on your original question.
note: You will find other sources mentioning the __autoload($class) function. This function does exactly the same, but will be removed from PHP in future updates. Therefore, you are better off using spl_autoload_register
Because I posted my Autoloader in the comments.
https://github.com/ArtisticPhoenix/MISC/blob/master/Autoloader.php
You can find the code at the bottom of this post:
The basic usage is as follows:
require_once 'Autoloader.php';
AutoLoader::getInstance()->regesterPath('\\', __DIR__);
This assumes it's in the directory that is the root of you namespace. So if you have a class.
namespace App;
class Database{ ... }
And this class is in
www
|- Autoloader.php
|- App
| --Database.php
Then it will look for __DIR__ + Namespace or __DIR__/APP/. You can register paths because if you have this setup.
www
|- Autoloader.php
|- includes
|-- App
| ---Database.php
Where the class is located in includes/App and Autoloader is in / the root folder you can do it this way.
require_once 'Autoloader.php';
AutoLoader::getInstance()->regesterPath('\\', __DIR__.'/includes/');
Also if you have it setup like this.
www
|- Autoloader.php
|- includes
| --Database.php
Where there is no actual App folder then you can do it like this.
require_once 'Autoloader.php';
AutoLoader::getInstance()->regesterPath('\\App\\', __DIR__.'/includes/');
Or any combination of the above.
It will account for the differences between \\App. App and \\App\\ for the most part. But you can also turn on debugging with this.
require_once 'Autoloader.php';
$AutoLoader = AutoLoader::getInstance();
$AutoLoader->setDebug(true);
$AutoLoader>regesterPath('\\App\\', __DIR__.'/includes/');
And it will spit out a bunch of stuff telling you where it is looking. You may have to use <pre> to keep the whitespace format, if your working in HTML. Conveniently you can also turn off Debugging, because you may be autoloading a bunch of classes.
$AutoLoader->setDebug(false);
You can also assign multiple paths, and give them a priority and it will look in them in the order of the priority. But that is not really important in this case.
So for example:
I have a folder
www
|-MISC
|--index.php
|--Autoloader.php
|---IMAP
|----GmailClient.php
Which is also in the same Git Repo. It has the namespace of Lib\Email\IMAP of which only the IMAP exists.
And to load It if I do this in index.php, which is inside the MISC file and at the same level as the AutoLoader.php file:
//include the Autoloader
require_once __DIR__.'/Autoloader.php';
//get an instance of it (Singleton pattern)
$Autoloader = Autoloader::getInstance();
//regester a namespace, path pair
$Autoloader->regesterPath('Lib\Email', __DIR__.'/IMAP/');
//preserve whitespace
echo "<pre>";
//turn on debugging before a problem class
$Autoloader->setDebug(true);
//Attempt to load the class as normal
$G = new GmailClient($hostname, $username, $password);
//turn off debugging after trying to load a problem class.
$AutoLoader->setDebug(false);
This is what it Outputs for debugging
================================= Autoloader::debugMode ==================================
Autoloader::splAutoload Lib\Email\IMAP\GmailClient
Checking class: GmailClient
Checking namespace: Lib/Email/IMAP
checking pathname:C:/Server/www/MISC/IMAP/IMAP/GmailClient.php
==========================================================================================
Right away we can see C:/Server/www/MISC/IMAP/IMAP/GmailClient.php That IMAP is in there 2x. This is because I included that in the path, So it starts looking in C:/Server/www/MISC/IMAP/ and then adds the namespace that wasn't given in the namespace arg IMAP from Lib/Email/IMAP. We gave it Lib/Email as the first argument. So essentially, because this was part of the path it's already in that folder when it looks for that.
So If I just remove that IMAP from the path:
$Autoloader->regesterPath('Lib\Email', __DIR__);
It will output this:
================================= Autoloader::debugMode ==================================
Autoloader::splAutoload Lib\Email\IMAP\GmailClient
Checking class: GmailClient
Checking namespace: Lib/Email/IMAP
checking pathname:C:/Server/www/MISC/IMAP/GmailClient.php
Found: C:/Server/www/MISC/IMAP/GmailClient.php
==========================================================================================
With the most important line being this
Found: C:/Server/www/MISC/IMAP/GmailClient.php
Which obviously means that it found the class file and loaded it.
Hope that makes sense.
Following is the full code for the autoloader, that way the answer wont break if anything changes with the Repo I linked.
<?php
/**
*
* (c) 2016 ArtisticPhoenix
*
* For license information please view the LICENSE file included with this source code. GPL-3
*
* PSR4 compatible Autoloader
*
* #author ArtisticPhoenix
* #see http://www.php-fig.org/psr/psr-4/
*
* #example
* $Autoloader = Autoloader::getInstance();
* //looks in includes for folder named /includes/Lib/Auth/User/
* $Autoloader->regesterPath('Lib\\Auth\\User', __DIR__.'/includes/');
*
*/
final class Autoloader
{
/**
*
* #var int
*/
const DEFAULT_PRIORITY = 10;
/**
* namespace / class path storage
* #var array
*/
private $paths = array();
/**
* cashe the loaded files
* #var array
*/
private $files = array();
/**
* namespace / class path storage
* #var array
*/
private $debugMode = false;
/**
*
* #var Self
*/
private static $instance;
/**
* No public construction allowed - Singleton
*/
private function __construct($throw, $prepend)
{
spl_autoload_register(array( $this,'splAutoload'), $throw, $prepend);
}
/**
* No cloning of allowed
*/
private function __clone()
{
}
/**
*
* Get an instance of the Autoloader Singleton
* #param boolean $throw
* #param boolean $prepend
* #return self
*/
public static function getInstance($throw = false, $prepend = false)
{
if (!self::$instance) {
self::$instance = new self($throw, $prepend);
}
return self::$instance;
}
/**
* set debug output
* #param boolean $debug
* #return self
*/
public function setDebug($debug = false)
{
$this->debugMode = $debug;
return $this;
}
/**
* Autoload
* #param string $class
*/
public function splAutoload($class)
{
$this->debugMode('_START_');
$this->debugMode(__METHOD__.' '.$class);
//keep the orignal class name
$_class = str_replace('\\', '/', $class);
$namespace = '';
if (false !== ($pos = strrpos($_class, '/'))) {
$namespace = substr($_class, 0, ($pos));
$_class = substr($_class, ($pos + 1));
}
//replace _ in class name only
if (false !== ($pos = strrpos($_class, '/'))) {
if (strlen($namespace)) {
$namespace .= '/'.substr($_class, 0, ($pos));
} else {
$namespace = substr($_class, 0, ($pos));
}
$_class = substr($_class, ($pos + 1));
}
$this->debugMode("Checking class: $_class");
$this->debugMode("Checking namespace: $namespace");
do {
if (isset($this->paths[ $namespace ])) {
foreach ($this->paths[ $namespace ] as $registered) {
$filepath = $registered['path'] . $_class . '.php';
$this->debugMode("checking pathname:{$filepath}");
if (file_exists($filepath)) {
$this->debugMode("Found: $filepath");
$this->debugMode('_END_');
require_once $filepath;
$this->files[$class] = $filepath;
}
}
}
if (strlen($namespace) == 0) {
//if the namespace is empty and we couldn't find the class we are done.
break;
}
if (false !== ($pos = strrpos($namespace, '/'))) {
$_class = substr($namespace, ($pos + 1)) . '/' . $_class;
$namespace = substr($namespace, 0, ($pos));
} else {
$_class = (strlen($namespace) ? $namespace : '') . '/' . $_class;
$namespace = '';
}
} while (true);
$this->debugMode('_END_');
}
/**
* get the paths regestered for a namespace, leave null go get all paths
* #param string $namespace
* #return array or false on falure
*/
public function getRegisteredPaths($namespace = null)
{
if (is_null($namespace)) {
return $this->paths;
} else {
return (isset($this->paths[$namespace])) ? array($namespace => $this->paths[$namespace]) : false;
}
}
/**
*
* #param string $namespace
* #param string $path
* #param int $priority
* #return self
*/
public function regesterPath($namespace, $path, $priority = self::DEFAULT_PRIORITY)
{
$namespace = str_replace('\\', '/', $namespace); //convert to directory seperator
$path = ($this->normalizePath($path));
$this->paths[$namespace][sha1($path)] = array(
'path' => $path,
'priority' => $priority
);
$this->sortByPriority($namespace);
return $this;
}
/**
* un-regester a path
* #param string $namespace
* #param string $path
*/
public function unloadPath($namespace, $path = null)
{
if ($path) {
$path = $this->normalizePath($path);
unset($this->paths[$namespace][sha1($path)]);
} else {
unset($this->paths[$namespace]);
}
}
/**
* check if a namespace is regestered
* #param string $namespace
* #param string $path
* #return bool
*/
public function isRegistered($namespace, $path = null)
{
if ($path) {
$path = $this->normalizePath($path);
return isset($this->paths[$namespace][sha1($path)]) ? true : false;
} else {
return isset($this->paths[$namespace]) ? true : false;
}
}
/**
* get the file pathname of a loaded class
* #param string $class
* #return mixed
*/
public function getLoadedFile($class = null)
{
if (!$class) {
return $this->files;
}
if (isset($this->files[$class])) {
return $this->files[$class];
}
}
/**
* output debug message
* #param string $message
*/
protected function debugMode($message)
{
if (!$this->debugMode) {
return;
}
switch ($message) {
case '_START_':
echo str_pad("= ".__METHOD__." =", 90, "=", STR_PAD_BOTH) . PHP_EOL;
break;
case '_END_':
echo str_pad("", 90, "=", STR_PAD_BOTH) . PHP_EOL . PHP_EOL;
break;
default:
echo $message . PHP_EOL;
}
}
/**
* sort namespaces by priority
* #param string $namespace
*/
protected function sortByPriority($namespace)
{
uasort($this->paths[$namespace], function ($a, $b) {
return ($a['priority'] > $b['priority']) ? true : false;
});
}
/**
* convert a path to unix seperators and make sure it has a trailing slash
* #param string $path
* #return string
*/
protected function normalizePath($path)
{
if (false !== strpos($path, '\\')) {
$path = str_replace("\\", "/", $path);
}
return rtrim($path, '/') . '/';
}
}
P.S. the GmailClient class I used in this example, I wrote to parse incoming Emails in a Gmail account. It's not 100% fleshed out as we needed it for a specific purpose. But it is in the same GitHub repository.
this is the tpl file used show GUI of something. i posted it all. how this code read the tpl file and show HTML display. which command is doing this.
class Template {
/**
* Config
*
* #access private
*/
private static $config;
/**
* Path templates
*
* #access private
*/
private $tpl_path = null;
/**
* Values
*
* #access private
*/
private $values = array();
/**
* Constructor
*
* #access public
*/
public function __construct($tpl_path) {
self::$config = config_load('template');
$this->tpl_path = $tpl_path;
}
/**
* Set a template variable
*
* #access public
*/
public function set($name, $value = null) {
if(is_array($name)) {
foreach ($name as $key => $value) {
$this->values[$key] = $value;
}
} else {
$this->values[$name] = $value;
}
}
/**
* Display the template file
*
* #access public
*/
public function display($template) {
if ($this->values) {
extract($this->values);
}
if (file_exists($this->tpl_path . $template . self::$config['template_extension'])) {
ob_start();
include($this->tpl_path . $template . self::$config['template_extension']);
$output = ob_get_contents();
ob_end_clean();
} else {
die('Template file '. $this->tpl_path . $template . self::$config['template_extension'] . ' not found.');
}
if ($output) echo $output;
}
}
?>
i have the file which is using this class and the code is
$tpl->set('customer_details', $customer_details);
$tpl->set('customer_addresses', $customer_addresses);
$tpl->set('countries', $countries);
//Display the template
$tpl->display('edit_account');
the above code is using tpl and setting values. but how the values are set and displayed
It's very simple. The class code uses php include. The templates can freely intermix php and HTML just as any php file can, and variables will be interpolated. The class uses output buffering to store the output and then using echo returns the output. Variables used in the class are provided generically using the set method that puts them all into a class array variable named 'values' which is undoubtably referenced in the template files.
I have a console command that runs a helper class and I want to write output with $this->info() to the console from the helper class.
My code looks like this:
App/Http/Console/Commands/SomeCommand.php
function handle(Helper $helper)
{
return $helper->somefunction();
}
App/Http/SomeHelper.php
function somefunction()
{
//some code
$this->info('send to console');
}
Is there any way to write the output to console from the helper?
I finally figured this out (works in Laravel 5.6)
In the handle() function of your SomeCommand class, add $this->myHelper->setConsoleOutput($this->getOutput());.
In your helper class, add:
/**
*
* #var \Symfony\Component\Console\Style\OutputStyle
*/
protected $consoleOutput;
/**
*
* #param \Symfony\Component\Console\Style\OutputStyle $consoleOutput
* #return $this
*/
public function setConsoleOutput($consoleOutput) {
$this->consoleOutput = $consoleOutput;
return $this;
}
/**
*
* #param string $text
* #return $this
*/
public function writeToOuput($text) {
if ($this->consoleOutput) {
$this->consoleOutput->writeln($text);
}
return $this;
}
/**
*
* #param string $text
* #return $this
*/
public function writeErrorToOuput($text) {
if ($this->consoleOutput) {
$style = new \Symfony\Component\Console\Formatter\OutputFormatterStyle('white', 'red', ['bold']); //white text on red background
$this->consoleOutput->getFormatter()->setStyle('error', $style);
$this->consoleOutput->writeln('<error>' . $text . '</error>');
}
return $this;
}
Then, anywhere else in your helper class, you can use:
$this->writeToOuput('Here is a string that I want to show in the console.');
You need to write $this->info('send to console'); this in SomeCommand.php file.For writing output follow this https://laravel.com/docs/5.2/artisan#writing-output
I have not tried, but maybe: $this->line("sent to console');
I have a sample class attached, that when I try to generate the test file for it, using the PHPUnit-Skelgen 1.2.0 on PHP 5.4.11, I do not get the namespace added to the file, so the test fails. However, all the methods are picked up.
Source File:
<?php
namespace lib\Parameters;
class COMMAND_LINE implements \Iterator
{
private $CommandLineOptions_ = array();
/**
* Create the Command Line Options Class
* #param boolean $AddHelpOption - Optionally add the -h/--help option automatically
*/
public function __construct($AddHelpOption = TRUE)
{
if( $AddHelpOption == TRUE)
{
}
}
/**
* An internal pointer to the current position in the Command Line Options
* #var integer
*/
protected $Position = 0;
/**
* This method takes the pointer back to the beginning of the dataset to restart the iteration
*/
public function rewind()
{
$this->Position = 0;
}
/**
* This method returns the value at the current position of the dataset
* #return COMMAND_LINE_OPTION
*/
public function current()
{
return $this->CommandLineOptions_[$this->Position];
}
/**
* Return the current value of the pointer
* #return integer
*/
public function key()
{
return $this->Position;
}
/**
* Move the pointer to the next element
*/
public function next()
{
++ $this->Position;
}
/**
* Returns where the next item is valid or not
* #return boolean
*/
public function valid()
{
return isset($this->CommandLineOptions_[$this->Position]);
}
}
?>
Command line, with the class in the current directory:
>phpunit-skelgen --test -- lib\Parameters\COMMAND_LINE COMMAND_LINE.class COMMAND_LINE_Test COMMAND_LINE.class.test
PHPUnit Skeleton Generator 1.2.0 by Sebastian Bergmann.
Wrote skeleton for "COMMAND_LINE_Test" to "COMMAND_LINE.class.test".
Test class created has the functions, but does not have the Namespace:
<?php
/**
* Generated by PHPUnit_SkeletonGenerator 1.2.0 on 2013-02-04 at 17:50:23.
*/
class COMMAND_LINE_Test extends PHPUnit_Framework_TestCase
{
/**
* #var COMMAND_LINE
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
$this->object = new COMMAND_LINE;
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}
/**
* #covers lib\Parameters\COMMAND_LINE::rewind
* #todo Implement testRewind().
*/
public function testRewind()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* #covers lib\Parameters\COMMAND_LINE::current
* #todo Implement testCurrent().
*/
public function testCurrent()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* #covers lib\Parameters\COMMAND_LINE::key
* #todo Implement testKey().
*/
public function testKey()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* #covers lib\Parameters\COMMAND_LINE::next
* #todo Implement testNext().
*/
public function testNext()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* #covers lib\Parameters\COMMAND_LINE::valid
* #todo Implement testValid().
*/
public function testValid()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
}
Trying it from the root directory with relative file paths does not work either. If I use a fully qualified namespace (which I can not, as I need to be relative), then the class is not located, and only the setUp and tearDown methods are found.
You can give namespace to your generated test class like this.
>phpunit-skelgen --test -- lib\Parameters\COMMAND_LINE COMMAND_LINE.class lib\Parameters\COMMAND_LINE_Test COMMAND_LINE.class.test