Imagine a function like this
function mytrim($str,$where)
{
if ($where=="left") return ltrim($str)
if ($where=="right") return rtrim($str)
}
Its very important to me show what is the options of the where in this functions (right or left)
The best way I found to do it is
/**
* #param string $str
* #param left|right $where
* #return mixed|string
*/
will show "where: \left|\right"
There is a better way to do it ?
Leaving aside that I don't think you should have this function in the first place, you could make use of type hints and return types to document the arguments instead of using PHPDoc:
<?php declare(strict_types=1);
function mytrim(string $str, string $where): ?string
{
if ($where=="left") return ltrim($str);
if ($where=="right") return rtrim($str);
}
Docs tend to get outdated fast while function signatures are always current.
Now you could argue that this will not tell you what you can provide for $where and you'd still need the PHPDoc to know how to ltrim and rtrim. You could utilize constants for that though, e.g.
<?php declare(strict_types=1);
const MYTRIM_LEFT = 'left';
const MYTRIM_RIGHT = 'right';
function mytrim(string $str, string $where): ?string
{
if ($where === MYTRIM_LEFT) return ltrim($str);
if ($where === MYTRIM_RIGHT) return rtrim($str);
}
You'd still need to know that these constants exist though. That's why I'd probably convert your mytrim to a Strings class and define the constants declarations in there by. And/Or you add a check into the function that throws an error if the $where isn't "left" or "right".
As of PHP 8.1 you could use enums to make it even more clear:
<?php declare(strict_types=1);
enum MyTrim {
case Left;
case Right;
}
function mytrim(string $str, MyTrim $where): ?string
{
if ($where === MyTrim::Left) return ltrim($str);
if ($where === MyTrim::Right) return rtrim($str);
}
echo mytrim(" test", MyTrim::Left); // prints "test"
Everything there is to know about how to use the code is now directly visible in the signature.
But coming back to my initial remark: you should not have this function in the first place. Since you need to specify whether it's a left or right trim, you don't win anything over directly using ltrim and rtrim in your code.
Related
I have some methods that can return one of two return types. I'm using a framework utilizing MCV so refactoring these few functions in particular is not appealing.
Is it possible to declare the return type returning one or the other and failing on anything else?
function test(): ?
{
if ($this->condition === false) {
return FailObject;
}
return SucceedObject;
}
As of PHP 8+, you may use union types:
function test(): FailObject|SuccessObject {}
Another way, available in all versions since PHP 4, is for the two objects to share an interface. Example:
interface ReturnInterface {}
class FailObject implements ReturnInterface {}
class SuccessObject implements ReturnInterface {}
function test(): ReturnInterface {}
In this example, ReturnInterface is empty. Its mere presence supports the needed return type declaration.
You could also use a base, possibly abstract, class.
To me, for this use case, interfaces are more clear and more extensible than union types. For example, if I later want a WarnObject I need only to define it as extending ReturnInterface -- rather than going through all signatures and updating them to FailObject|SuccessObject|WarnObject.
As noted by bishop, there is an RFC for adding multiple return types. However, I thought I'd add that as of PHP7.1 you can now specify a nullable return type like this:
function exampleFunction(string $input) : ?int
{
// Do something
}
So this function would take in a string and by adding the question mark before int you are allowing it to return either null or an integer.
Here's a link to the documentation:
http://php.net/manual/en/functions.returning-values.php
And here's a quote from that page explaining the usage:
PHP 7.1 allows for void and null return types by preceding the type declaration with a ? — (e.g. function canReturnNullorString(): ?string)
Also, here's another thread that relates to this: Nullable return types in PHP7
PHP from 7.2 onward supports the object return type
http://php.net/manual/en/migration72.new-features.php
function test(object $obj) : object
// return any type of object ...
Since PHP 8.0 this is possible.
You can now use union types to specify this:
function test(): Success|Failure
{
if ($this->condition === false) {
return new Failure();
}
return new Success();
}
The fact that this is possible does not mean that it is always advisable. In many (probably most) situations, using an interface (e.g. Result, which both Failure and Failure would implement) as advised in a different answer, is still much preferable.
But there are other instances where union types could make sense to an alternative to weak typing. E.g. a method that accepts both string and int, or to describe the return type of a function like stripos(), which returns int|false.
This ins't correct way:
function test(): ?
{
if ($this->condition === false) {
return FailObject;
}
return SucceedObject;
}
Multiple return type is a bad practice. Good practices:
You should define a exception:
class FailObjectException extends \Exception
{
private $exampleExtraInfo;
public function __construct($exampleExtraInfo, $message)
{
parent::__construct($message);
$this->exampleExtraInfo = $exampleExtraInfo;
}
public function exampleExtraInfo(): int
{
return $this->exampleExtraInfo;
}
}
Now, you can define function like:
function test(): SucceedObject
{
if ($this->condition === false) {
throw new FailObjectException(...,...);
}
return SucceedObject;
}
And use this function with try/catch:
try{
$succeedObject = $any->test();
} catch (FailObjectException $exception){
//do something
}
What can I use other than if(!empty( $product->a_funky_function() )) to check if the method is empty before calling it?
I've tried method_exists() and function_exists() and a whole plethora of conditions. I think the issue is that I need to have my $product variable there.
Please help.
A fairly common pattern is to have two methods on your class, along the lines of getField and hasField. The former returns the value, and the latter returns true or false depending whether or not the value is set (where "set" can mean not null, or not empty, or whatever else you might want it to mean).
An example:
class Foo
{
/** #var string */
private $field;
/**
* #return string
*/
public function getField()
{
return $this->field;
}
/**
* #return bool
*/
public function hasField()
{
return $this->getField() !== null;
}
}
This would then be used like:
if ($foo->hasField()) {
$field = $foo->getField();
...
}
Often, like in this example, the has... method often just delegates to the get... method internally, to save duplicating the logic. If the getter contains particularly heavy processing (e.g. a database lookup or API call), you might want to factor in ways to avoid performing it twice, but that's a bit out of scope.
You need absolutely to call it so it can compute the value which it will return, so I think there is no other way to know if it will return something not empty without calling it...
You have to call it.
$result = $product->getFunction();
if(!empty($result)) {
//your code here
}
So I'm a newbie at laravel framework and PyroCMS, I recently install PyroCMS and go to mydomain.com/register to try the registration function and I got the following error, but no idea how to solve it.
Error code:
}
/**
* Convert the given string to upper-case.
*
* #param string $value
* #return string
*/
public static function upper($value)
{
return mb_strtoupper($value, 'UTF-8');
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Highlighted line
}
/**
* Convert the given string to title case.
*
* #param string $value
* #return string
*/
public static function title($value)
{
return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
}
And
public static function substr($string, $start, $length = null)
{
return mb_substr($string, $start, $length, 'UTF-8');
}
Which the line return mb_strtoupper($value, 'UTF-8'); and return mb_substr($string, $start, $length, 'UTF-8'); is highlighted with an error log expects parameter 1 to be string, array given.
How can I solve it?
More info:
Error file: pyrocms/vendor/laravel/framework/src/Illuminate/Support/Str.php
Error screenshot:
You may or may not want to do something like this
public static function upper($value)
{
if(is_array($value)){
foreach($value as &$item){
$item = self::upper($item); //recursive
}
return $value;
}
return mb_strtoupper($value, 'UTF-8');
}
This will recursively call upper when it's an array and the effect will be that it will uppercase everything in $value. This may or may not be what you want.
Chances are you are passing the wrong data to this method.
To pass the right data you would do something like
$value['key'] = Class::upper($value['key']);
But I have no idea what value key should be, and I don't even know what class these methods reside in Class. I don't use laravel and I never heard of PyroCMS before.
If I used every framework that came along i would spend my days learning frameworks and not building them. I don't use laravel because I don't like the syntax blade uses, I am not a fan of query builders, probably these can be replaced but we already have everything build in a different framework. So there is no need to customize it to work the way I want it to when we already have a solution that works just fine.
That said, I do know PHP, I know it quite well in fact.
I have a data import class with many private functions. In most of functions there is json string handle to import data, so I don't have anything to return. Now what is the best practice to set as return of such functions?
Is return; or return true; correctly?
I think, if you don't need to return any value - you must not use return; nor return true;.
In this case your function works not as pure function (it performs some mutation, or update some data in database or in cache or print data into output or stream etc) and it's perfectly ok!) But if you need this return for unit-tests - please don't do it, because it's ridiculous. Test your real behavior, check mutation, check output and so on and so forth...
A return value is not required by a PHP function. It is however, best practice to add a docblock that sais so:
/**
* <function description?
*
* #return void
**/
function doSomething()
{
}
Source: http://php.net/manual/en/functions.returning-values.php
Note:
If the return is omitted the value NULL will be returned.
It's a good practice to use CQS (Command-query separation) approach. It states that every method should either be a command or a query, but not both. So the difference is:
Command - method that changes object state and should not return anything;
Query - method that return some data, but doesn't change object state.
<?php
class Foo
{
private $str = '';
// this is a query
public function getData() : string
{
return $this->str;
}
// this is a command
public function setData(string $str) : void
{
$this->str = $str
}
}
So, in your case functions for importing data are commands and should not return anything.
I stumbled across this page which talks about the very handy new reflection class that ships with PHP5 and returns all the methods and properties of a class:
print_r to get object methods in PHP?
Following on from this, is there any way to determine the allowable values for the methods it returns?
You mean determine the allowed types of return values, and range of return values, for some method in some class? Very very hardly, I think. After all, there is no way of defining or hinting a return value in PHP. So I could do the following:
class .... {
function myFunction()
{
... some code ....
if (condition) return "A";
.... more code .....
if (condition2) return 2;
.... more code ....
if (condition3) return $_POST["number"];
}
}
this is a totally screwed-up example of course, but you get my point. The possible types of the return value are extremely hard to predict because I could return anything at any point.
I think the best one can do is solve this in documentation blocks. If you follow phpDoc notation:
/**
* #desc Searches for a record.
* #return int the number of found records.
*/
function myMethod()
{ ....
many IDEs are able to at least give you a hint about the expected return type when you type a call to the method.
Well, it depends on what you mean by "allowable values". If it's available in the doc-block, you can probably find it with Reflection... To find the return value:
class foo {
/**
* #returns boolean When false
*/
public function bar($x, $y = 'bar') {
}
}
$reflector = new ReflectionMethod('foo', 'bar');
$comment = $reflector->getDocComment();
if (preg_match('##returns (\\S+)#', $comment, $match)) {
echo "Method returns: ".$match[1];
}
produces:
Method Returns: boolean
Then, you'd just need to parse out what you want from that doc comment... Note it can be a type OR a class (or a grouping of multiple, boolean|null|string|DOMNode)...