phpspec testing file contents / file operations - php

I'm curious about the best way of speccing classes that handle file operations.
Assuming I have a fictional class with a method duplicate whose job is to duplicate the contents of a file.
<?php
class FileOperator
{
public function duplicate($filename)
{
$content = file_get_contents($filename);
file_put_contents($filename, $content.$content);
}
}
I know that I can use something like vfsStream to assert the change without touching the actual filesystem (at least with assertions in PHPUnit).
How could I assert that in a spec? Or would it be approached differently?
Also, I get that I might want to extract that functionality into another class and use a Spy to assert that the FileOperator calls its dependency correctly, but then I'd still have to spec that adapter class, and my question remains.
Thanks.

This is more likely a functional test rather than an unit test, so it's hard to use phpspec in this case.
If you insist, I see two options.
If you happen to need a method to fetch the file contents too, you could write your spec this way:
use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\vfsStreamDirectory;
use PhpSpec\ObjectBehavior;
class FileOperatorSpec extends ObjectBehavior
{
/**
* #var vfsStreamDirectory
*/
private $workDir;
function let()
{
$this->workDir = vfsStream::setup('workDir');
}
function it_duplicates_a_content_in_a_file()
{
$this->createFile('foo', 'bar');
$this->duplicate('vfs://workDir/foo');
$this->read('vfs://workDir/foo')->shouldReturn('barbar');
}
private function createFile($path, $content)
{
$file = vfsStream::newFile($path);
$file->setContent($content);
$this->workDir->addChild($file);
}
}
Alternatively, you could use the expect helper:
use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\vfsStreamDirectory;
use PhpSpec\ObjectBehavior;
class FileOperatorSpec extends ObjectBehavior
{
/**
* #var vfsStreamDirectory
*/
private $workDir;
function let()
{
$this->workDir = vfsStream::setup('workDir');
}
function it_duplicates_a_content_in_a_file()
{
$this->createFile('foo', 'bar');
$this->duplicate('vfs://workDir/foo');
expect(file_get_contents('vfs://workDir/foo'))->toBe('barbar');
}
private function createFile($path, $content)
{
$file = vfsStream::newFile($path);
$file->setContent($content);
$this->workDir->addChild($file);
}
}

Related

Best way to avoid reloading from cache repititively?

Consider having a Class whose method is being called in iteration n times, and the method in itself has a statement that pulls data from a cache system - Redis.
Now if this method is called n times the cache is also hit n times, leading to a continuous fetch and unserializing of the same data n times, which in the case of an interpreter like PHP consumes a good amount of time & CPU.
Passing the cached data to this method could also be a no, as we might instantiate n number of instances of this class.
Hence is there a way where we can avoid hitting the cache multiple times in the context of a Class and/or Object?
Maybe we can somehow use static properties of the Object to hold the value?
First, write a service class that:
Provides getter, which:
Loads required value from cache (based on unique-key),
But also backups the loaded-value,
Finally, returns backupped-value instead of re-loading from cache.
Provides setter which updates both backup and cache.
Then simply use Laravel's feature to inject and/or get instance of said service-class.
Example
<?php
namespace App\Services;
use Illuminate\Support\Facades\Cache;
class MyCacheService
{
protected $backupMap = [];
/**
* Requests an instance of this-class from Laravel.
*
* #return MyCacheService|null
*/
public static function instance()
{
return \Illuminate\Support\Facades\App::make(MyCacheService::class);
}
public function get($key)
{
$backup = & $this->backupMap[$key];
if ( ! isset($backup)) {
$backup = $this->rawGet($key);
}
return $buckup;
}
public function set($key, $value)
{
$this->rawSet($key, $value);
$this->backupMap[$key] = $value;
}
/** Loads from cache */
private function rawGet($key)
{
// ... your normal loading from cache logic goes here,
// but as sub-example:
return Cache::get($key);
}
/** Saves into cache */
private function rawSet($key, $value)
{
// ... your normal saving into cache logic goes here,
// but as sub-example:
Cache::set($key, $value);
}
}
Usage:
use App\Services\MyCacheService;
use Illuminate\Support\Facades\App;
// ...
// Get instance once, like:
$cache = MyCacheService::instance();
// Or like:
/** #var MyCacheService|null $cache */
$cache = App::make(MyCacheService::class);
// Finally, reuse as many times as required, like:
$myValue = $cache->get('my-unique-key');
$myOtherValue = $cache->get('my-other-key');
Note that Laravel stores the class's instance for us, else one could use private static property named $instance, and return that from instance() method.
WARNING: as you may already know, maybe replace rawGet and rawSet's logic.
Also, place MyCacheService.php file (which's source is above) in app/Services folder.
If you are in the opinion that you should not use a service-class (which the other answer explains),
then consider using PHP's $GLOBALS variable to backup the loaded-Cache (instead of adding yet another custom static variable).
Examples
Maybe backup once for all:
In Laravel's AppServiceProvider.php file, do something like:
<?php
namespace App\Providers;
// ...
use Illuminate\Support\Facades\Cache;
class AppServiceProvider extends ServiceProvider
{
// ...
public function boot()
{
$keyList = [
'my-unique-key',
'my-other-key',
];
foreach($keyList as $key) {
$GLOBALS['cache-' . $key] = Cache::get($key);
}
}
}
Finally, use $GLOBALS['cache-my-unique-key'] anywhere required.
Or, backup per class:
<?php
use Illuminate\Support\Facades\Cache;
class YourClass {
private $myData;
public function __construct()
{
$key = 'my-unique-key';
$value = & $GLOBALS['cache-' . $key];
if ( ! isset($value)) {
$value = Cache::get($key);
}
$this->myData = $value;
}
// Finally, use `$this->myData` anywhere in this same class.
}
Note that as you may already know, in both cases we use 'cache-' prefix to not overwrite anything by mistake.
You can use static properties to store the cached data and use access it same.
class CacheClass
{
private static $cachedData;
public function retrieveData()
{
if (!isset(self::$cachedData)) {
// re-assign the value
self::$cachedData = fetchDataFromCache();
}
return self::$cachedData;
}
}

How can I create an array of mocks in PHPSpec?

I've just started using PHPSpec and I'm really enjoying it over PHPUnit, especially the no-effort mocks and stubs. Anyway, the method I'm trying to test expects an array of Cell objects. How can I tell PHPSpec to give me an array of mocks?
Simplified version of my class
<?php
namespace Mything;
class Row
{
/** #var Cell[] */
protected $cells;
/**
* #param Cell[] $cells
*/
public function __construct(array $cells)
{
$this->setCells($cells);
}
/**
* #param Cell[] $cells
* #return Row
*/
public function setCells(array $cells)
{
// validate that $cells only contains instances of Cell
$this->cells = $cells;
return $this;
}
}
Simplified version of my test
<?php
namespace spec\MyThing\Row;
use MyThing\Cell;
use PhpSpec\ObjectBehavior;
class RowSpec extends ObjectBehavior
{
function let()
{
// need to get an array of Cell objects
$this->beConstructedWith($cells);
}
function it_is_initializable()
{
$this->shouldHaveType('MyThing\Row');
}
// ...
}
I had hoped I could do the following, but it then complains that it can't find Cell[]. Using the FQN it complains about not being able to find \MyThing\Cell[].
/**
* #param Cell[] $cells
*/
function let($cells)
{
// need to get an array of Cell objects
$this->beConstructedWith($cells);
}
The only options I can work out is to pass multiple type-hinted Cell arguments and manually combine them into an array. Am I missing something simple?
Edit: I'm using PHPSpec 2.5.3 as, unfortunately the server is currently stuck at PHP 5.3 :-(
Why don't you do something like
use Prophecy\Prophet;
use Cell; // adapt it with PSR-4 and make it use correct class
class RowSpec extends ObjectBehavior
{
private $prophet;
private $cells = [];
function let()
{
$this->prophet = new Prophet();
for ($i = 0; $i < 10; $i++) {
$this->cells[] = $this->prophet->prophesize(Cell::class);
}
$this->beConstructedWith($cells);
}
// ....
function letGo()
{
$this->prophet->checkPredictions();
}
public function it_is_a_dummy_spec_method()
{
// use here your cells mocks with $this->cells
// and make predictions on them
}
}
Explanation
In let function you instantiate a Prophet object that is, basically a mocking library/framework that is used in tandem with PHPSpec (that itself use Prophecy).
I suggest to keep the instance ($this->prophet) as will be useful for next steps.
Now, you have to create your mocks, and you can do with prophet and prophesize.
Even for the mocks, I suggest to keep them into a private variable that you probably use for predictions in your methods.
letGo function is here to check explicitly the expectations you have made on cells: without, cells are only stubs or dummies.
Of course it's handy to pass through method signature a mock and to skip checkPredictions explicitly but, as soon as you need an array of mocks, I suppose that this is the only way to reach your goal.

PHP Replacing static methods in Entities, best practices

<?php
class Entity {
/**
* #var array|stdClass|Collection|string
**/
private $mixed;
public function getMixedAsPhpArray(array $filter) {
return EntityHelper::toPhpArray($this->mixed, $filter);
}
}
Given the above class, how would you remove the static call to EntityHelper::toPhpArray assuming $mixed could by any of the types in the PHPDoc Block? (This is just a simplified example of an issue I'm facing where I have a function to take "dirty in and clean out") (I cannot add it to the Entity as many Entities need this function and cannot extend from an Abstrart Entity with this method as they already extend various others).
I'm using Symfony and thought of DI in the helper but replacing all new Entity with a call to the service container would be a bad (and slow) idea. Another idea would be to return the dirty output and use a service to clean and filter it but I also think that's a bad idea as it takes all the data from the Entity out into the Application then into the Service when I think it should happen in one go and remove mistakes (and maybe memory usage...).
The best solution will depend on your application, but one method you might use is to put your helper code in a trait:
<?php
trait ArrayCleaner {
public function toPhpArray($dataToFilter, $filterArray) {
// ... your code here
}
}
class Entity {
use ArrayCleaner;
/**
* #var array|stdClass|Collection|string
**/
private $mixed;
public function getMixedAsPhpArray(array $filter) {
return $this->toPhpArray($this->mixed, $filter);
}
}
http://php.net/manual/en/language.oop5.traits.php
Well, you could do this with a trait.
trait EntityHelperTrait
{
private function toPhpArray($value, $filter) {
// body of method
}
}
class Entity
{
use EntityHelperTrait;
/**
* #var array|stdClass|Collection|string
**/
private $mixed;
public function getMixedAsPhpArray(array $filter) {
return $this->toPhpArray($this->mixed, $filter);
}
}
But based on your (admittedly simplified) example, you're mixing responsibilities. The job of type conversion, which is what this essentially is, should belong somewhere else, not baked into the Entity class itself.
I think it's totally fine to let the Entity return the "dirty" output for another component to filter/clean/whatever.

Double Include Prevention Code in PHP Prevents Doxygen From Generating Documentation

I am writing relatively complex PHP applications and have several files for class definitions formatted as follows:
<?php
if(!class_exists("FooBar"))
{
/**
* This is documentation for the class FooBar
*/
class FooBar
{
/**
* Documentation for FooBar's constructor
*/
public function __construct() {
;
}
}
} // class_exists
This is to prevent multiple definition errors with complex class hierarchies and applications.
However, Doxygen does not document any classes that are specified this way. Commenting out or removing the if(!class_exists()) statement causes Doxygen to correctly document this class, but introduces errors with applications.
Is there any way I can force Doxygen to generate documentation for these classes?
As mentioned by #Sverri in the comments, I also think that autoloading is a good way to solve your problem (what it already did, as it seems).
But just for the case you (or someone else) is looking for a doxygen-only solution:
You can use INPUT_FILTERS in doxygen to remove or change some parts of the source code before creating the documentation.
You can use the following php code to remove all lines containing if(!class_exists and the braces { } that belong to this if-block, as long as it is not followed by another block.
// input
$source = file_get_contents($argv[1]);
// removes the whole line
list($head,$tail) = preg_split('/.*if\(!class_exists\(.+/', $source, 2);
$openingBracePos = strpos($tail,'{');
$closingBracePos = strrpos($tail,'}');
if($openingBracePos !== false && $closingBracePos !== false)
$source = $head . substr($tail,$openingBracePos+1,
$closingBracePos-$openingBracePos-1);
echo $source;
This filter truns
<?php
if(!class_exists("FooBar")) // This comment will also disappear
{
/**
* This is documentation for the class FooBar
*/
class FooBar
{
/**
* Documentation for FooBar\'s constructor
*/
public function __construct() {
;
}
}
} // class_exists
into
<?php
/**
* This is documentation for the class FooBar
*/
class FooBar
{
/**
* Documentation for FooBar's constructor
*/
public function __construct() {
;
}
}
Note: On windows I have to call the filter like this: C:\xampp\php\php.exe php_var_filter.php
You can find some more input filters to improve doxygen's php support on GitHub.

PHP: OOP and methods

I`ve been wondering how to implement methods in a class.
Could someone explain me what means if one does OOP in procedural style?
Here is an example:
class Fld extends Model {
private $file;
private $properties = array();
public function init($file) {
$this->file = $file;
$this->parseFile();
}
private function parseFile() {
// parses the file
foreach($this->file as $line) {
//..................
}
$this->properties = $result;
}
}
I mean is it a good thing to have methods like these that do operations for the class properties like that. Or should I pass the class property as method parameter...
I mean this would cause error if the file property wouldnt be declared.
If the file is mandatory for you object, it should be a parameter in your constructor.
class Fld extends Model {
private $file;
private $properties = array();
function __construct($file) {
$this->file = $file;
}
public function parse() {
foreach($this->file as $line) {
/* ... */
$this->properties = $result;
}
}
}
When there is a method in your class which does not use any of the class properties, you should think about making that method static or even create a separate class for this method.
I think people describe code as "OOP in procedural style" when the methods inside a class tend to be very long and complex.
Martin Fowler's book 'Refactoring', describes a long method as a 'code smell' that hints that parts of its code could be broken down into smaller methods or separated out into other classes.
see: http://books.google.co.uk/books?id=1MsETFPD3I0C&lpg=PP1&dq=refactoring&pg=PA76#v=onepage&q&f=false
I think your code is perfectly fine. Just bare in mind how disposable the objects of the class are. Generally a 'parsing service' like this should be created, used and thrown away. Then you won't have to worry about old properties causing confusion if it is re-used.
As eteubert suggests, passing the tooling in the constructor helps to let the clients know that the object is being created for a very particular purpose.

Categories