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.
Related
<?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.
Recently I ran into an interesting situation when implementing a PHP application using PhpStorm. The following code snippet illustrates the problem.
interface I{
function foo();
}
trait T{
/**
* #return string
*/
public function getTraitMsg()
{
return "I am a trait";
}
}
class A implements I{
use T;
function foo(){}
}
class C implements I{
use T;
function foo(){}
}
class B {
/**
* #param I $input <===Is there anyway to specify that $input use T?
*/
public function doSomethingCool($input){ //An instance of "A" or "C"
$msg = $input -> getTraitMsg(); //Phpstorm freaks out here
}
}
My question is in the comment. How do I indicate that $input parameter implements I and uses T?
It's a lit bit hackly, but you can use class_uses it returns list of used traits. And add T as a #param type in PHPDoc for autocomplete
class B {
/**
* #param I|T $input <===Is there anyway to specify that $input use T?
*/
public function doSomethingCool($input){ //An instance of "A" or "C"
$uses = class_uses(get_class($input));
if (!empty($uses['T'])) {
echo $input->getTraitMsg(); //Phpstorm freaks out here
}
}
}
AFAIK you cannot type hint a trait usage in such way (#param only accepts scalar types or classes/interfaces + some keywords).
The ideal solution for you would be placing getTraitMsg() declaration into I interface.
If this cannot be done .. then you can specify that only instances of A or C can be passed here (as they utilize that trait):
/**
* #param A|C $input
*/
public function doSomethingCool($input)
{
$msg = $input->getTraitMsg(); // PhpStorm is good now
}
If names of such possible classes are unknown in advance (e.g. it's a library code and final classes could be anything in every new project or even added in current project at any time) .. then I suggest to use safeguards, which you should be using with such code anyway (via method_exists()):
/**
* #param I $input
*/
public function doSomethingCool($input)
{
if (method_exists($input, 'getTraitMsg')) {
$msg = $input->getTraitMsg(); // PhpStorm is good now
}
}
Why use safeguard? Because you may pass instance of another class K that implements I but does not use trait T. In such case code without guard will break.
Just to clarify: you could use #param I|T $input to specify that method expects instance that implements I or uses T .. but it's only works for PhpStorm (not sure about other IDEs) -- AFAIK it's not accepted by actual PHPDocumentor and does not seem to fit the PHPDoc proposed standard.
"//Phpstorm freaks out here" -- no, it's not. It just tries to signal you that your code is not correct.
The contract of method doSomethingCool() doesn't require $input to expose any method named getTraitMsg(). The docblock says it should implement interface I but the docblock is not code, it only helps PhpStorm help you with validations and suggestions.
Because you didn't type-hinted the argument $input, the code:
$b = new B();
$b->doSomethingCool(1);
is valid but it crashes as soon as it tries to execute the line $msg = $input -> getTraitMsg();.
If you want to call getTraitMsg() on $input you have to:
declare the type of $input;
make sure the declared type of $input exposes a method named getTraitMsg().
For the first step, your existing code of class B should read:
class B {
/**
* #param I $input
*/
public function doSomethingCool(I $input) {
$msg = $input -> getTraitMsg();
}
}
Please remark the type I in front of argument $input in the parameters list.
The easiest way to accomplish the next step is to declare method getTraitMsg() into the interface I:
interface I {
function foo();
function getTraitMsg();
}
Now, the code:
$b = new B();
$b->doSomethingCool(1);
throws an exception when it reaches the line $b->doSomethingCool(1); (i.e. before entering the function). It is the PHP's way to tell you the method is not invoked with the correct arguments. You have to pass it an object that implements the interface I, no matter if it is of type A or C. It can be of any other type that implements interface I and nobody cares if it uses trait T to implement it or not.
I have the following class I want to test:
<?php
namespace Freya\Component\PageBuilder\FieldHandler;
use Freya\Component\PageBuilder\FieldHandler\IFieldHandler;
/**
* Used to get a set of non empty, false or null fields.
*
* The core purpose is to use this class to determine that A) Advanced Custom Fields is installed
* and B) that we get back a set of fields for a child page.
*
* The cavete here is that this class requires you to have child pages that then have Custom Fields that
* you want to display on each of those pages. getting other page information such as content, title, featured image
* and other meta boxes is left ot the end developer.
*
* #see Freya\Component\PageBuilder\FieldHandler\IFieldHandler
*/
class FieldHandler implements IFieldHandler {
/**
* {#inheritdoc}
*
* #return bool
*/
public function checkForAFC() {
return function_exists("register_field_group");
}
/**
* {#inheritdoc}
*
* #param $childPageID - the id of the child page who may or may not have custom fields.
* #return mixed - Null or Array
*/
public function getFields($childPageId) {
$fieldsForThisPage = get_fields($childPageId);
if (is_array($fieldsForThisPage)) {
foreach ($fieldsForThisPage as $key => $value) {
if ($value === "" || $value === false || $value === null) {
unset($fieldsForThisPage[$key]);
}
}
return $fieldsForThisPage;
}
return null;
}
}
I can test all of this but one thing I want to do is stub the get_fields() function to say you will return this type of array to then be used how ever the rest of the function uses it, which in this case is looping through it.
The part I don't know how to do in php is stub a function that's being called and then say you will return x.
So how do I stub get_fields?
You cen define such function in global namespace. Take a look at the following example:
namespace {
function getFields($pageId) {
return array($pageId);
}
}
namespace MyNamespace {
class MyClass
{
public function foo(){
var_dump(getFields(5));
}
}
$obj = new MyClass();
$obj->foo();
}
And here is the output:
array(1) {
[0]=>
int(5)
}
The only issue is that this function will exist till end of script. To solve this problem you can use the tearDown method together with runkit library:
http://php.net/manual/en/function.runkit-function-remove.php
that allows you to remove user defined functions.
Unfortunatelly this library does not exist on Windows so there you won't be able to remove the definition and may consider running tests in isolation.
Edit:
You can also consider using this library (it also depends on runkit):
https://github.com/tcz/phpunit-mockfunction
You can use a trick here with the unqualified function name get_fields(). Since you don't use the fully qualified function name \get_fields() PHP will first try to find the function in the current namespace and then fall back to the global function name.
For the definition of qualified and unqualified, see: http://php.net/manual/en/language.namespaces.basics.php (it's similar to absolute and relative filenames)
So what you have to do is define the function in the namespace of the class, together with your test case, like this:
namespace Freya\Component\PageBuilder\FieldHandler;
function get_fields()
{
return ['X'];
}
class FieldHandlerTest extends \PHPUnit_Test_Case
{
...
}
Additional notes:
You can do the same with core functions, as described here: http://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/
This trick only works with functions, not classes. Classes in the global namespace always must be referenced with leading backslash.
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);
}
}
I don't know how to phrase this one exactly but what I need is a way to load the type-hinting of a classes methods. Basically I have a base class that has a get function that looks like this:
class Base {
/**
*
* #param type $i
* #return \i
*/
public static function get($i) {
// make sure it exists before creating
$classes = get_declared_classes();
if (!in_array($i, $classes)) {
if (class_exists($i)) {
return new $i();
}
}
return $i;
}
}
Now for an example, say I had a class called test:
class test {
function derp() {
echo 'derp';
}
}
I'd instantiate the test object by something like this:
$test = base::get('test');
Now what I'd like to be able to do is as I type like this:
$test->
The methods (Currently only derp()) should be suggested, I've seen documents all around SO but they don't work :(
What's weird is that if I change the #return comment to the test class name then the suggestions work.
BUT it the classes are all not set, there could be different classes instantiated, hence why I tried #returns \i (suggested by netbeans). Is there any way to achieve this?
EDIT
The reason I need the type hinting is to allow for calling methods like the following:
base::get('test')->derp();
What always works is this:
/** #var ADDTYPEHERE $test */
$test = base::get('test');
What also works is this:
if ($test instanceof CLASS_IT_IS) {
// completion works in here
}
The solution you want can never work, since your IDE (netbeans) cannot know what class you instantiated, without any hint like one of the above.