I have a lot of code in some classes, when I must pre-declare array $keys = [];:
public function convertIdStringToMongoID($array_id = array())
{
$keys = [];
foreach ($array_id as $k => $id) {
$keys[] = new \MongoId($id);
}
return $keys;
}
It looks now good, how to make this more beauty?
According to this docs can try mapping your array, something like this:
public function convertIdStringToMongoID($array_id = array())
{
$func = function($id) {
return new \MongoId($id);
};
return array_map($func, $array_id);
}
Or according to this example, something like:
public function convertIdStringToMongoID($array_id = array())
{
return array_map(array($this, 'to_id'), $array_id);
}
private function to_id($id) {
return new \MongoId($id);
}
Both examples use functional programming approach.
The code you posted is clean and fast. You can write it in a more compact way using array_map() and an anonymous function:
public function convertIdStringToMongoID(array $array_id = array())
{
return array_map(function ($id) { return new \MongoId($id); }, $array_id);
}
Some people might consider it more beautiful, others will say it is slightly more difficult to read and understand it this way.
Both will agree that the execution time of this version is slightly longer than your version. However, the difference is not significant, there are other places to search for optimizations (access to disk and external resources, database queries, Mongo etc.)
Related
I am learning PHP using reviewing some complete PHP projects. (I know that this is a bad way, but my goal is not to be a PHP programmer!) Anyway, I faced with the following function that is weird a little to me:
function filterIt($filter): callable {
return function ($value) use ($filter) {
return filter_var($value, $filter) !== false;
};
}
I don't know what this function do and why it has been witter in such a way that a function is inside of another function! inner function returns something and main function also. Why we need such complicated function? or maybe one can make it simpler?
For this reason I want to write isEven() function as callable function like above. But I have no idea!
I don't know what that function do, but by mimicking from that:
function isEven($num): callable {
return function () use ($num) {
return $num % 2 == 0;
};
}
I couldn't debug this using var_dump or print_r .
Not sure how you are calling this in you're test, but as the function is actually returning a callable you would be able to debug it once you run it like this:
<?php
function isEven($num): callable {
return function () use ($num) {
return $num % 2 == 0;
};
}
var_dump(isEven(14)());
IMO the summary of the filterIt() function is "give me an anonymous function that filters based on $filter", and then that callable is likely passed somewhere else to be applied. I would venture to guess that the author wrote this function as a shorthand so that they did not need to write out the anonymous function definition over and over for different values of $filter.
Below is a simplified example of such behaviour:
class ExampleCollection {
protected $filters = [];
public function __construct(protected array $items) {}
public function addFilter(callable $filter) {
$this->filters[] = $filter;
}
public function getFiltered() :\Generator {
foreach($this->items as $item) {
foreach($this->filters as $filter) {
if( $filter($item) ) {
continue 2;
}
}
yield $item;
}
}
}
function makeMultipleFilter(int $value) :callable {
return function($a)use($value) {
return $a % $value === 0;
};
}
$c = new ExampleCollection([1,2,3,4,5,6,7,8,9,10]);
// add filters to exclude multiples of 3 and 4
$c->addFilter(makeMultipleFilter(3));
$c->addFilter(makeMultipleFilter(4));
foreach($c->getFiltered() as $item) {
printf("Got: %d\n", $item);
}
Output:
Got: 1
Got: 2
Got: 5
Got: 7
Got: 10
But I agree with Chris Haas' comment that the author's intent is not always obvious and is best asked of them, if possible. Further to that, not all code is exemplary, even if someone exemplary happens to have written it. Everyone writes themselves into a corner sometimes and has to resort to a confusing and/or ugly piece of code to get around it. Which is not to say that this is what that is, though it is somewhat confusing on first read.
So I'm really confused about anonymous functions in PHP, I want to know if anonymous functions are mainly used as a matter of taste or coding style.
I'm asking this because you can achieve the same result without a callback function and less code.
So here is some test code:
$output = 10;
$people = (new People(new John));
//VERSION 1 ANONYMOUS FUNCTION
$people->run(function ($value) use(&$output){
$output = $output + $value;
});
var_dump($output); //RESULT => 20
//VERSION 2 WITHOUT ANONYMOUS FUNCTION
var_dump($people->run() + $output); //RESULT => 30
You can run and see the full code here:
https://www.tehplayground.com/IhWJJU0jbNnzuird
<?php
interface HumanInterface
{
public function hello();
}
class People
{
protected $person;
protected $value;
public function __construct(HumanInterface $person)
{
$this->person = $person;
return $this;
}
public function run(callable $callback = null, $name = null)
{
$this->value = 10;
if(is_callable($callback)) {
return call_user_func($callback, $this->value);
}
return $this->value;
}
}
class John implements HumanInterface
{
public function hello()
{
return 'hi i am john';
}
}
$output = 10;
$people = (new People(new John));
$people->run(function ($value) use(&$output){
$output = $output + $value;
});
var_dump($output);
var_dump($people->run() + $output);
So my question is: why use an anonymous function? Is it a matter of
personal choice?
Anonymous functions or „Closures“ are very useful if it is used as a one-time callback. If you use PHP's usort-method for example. The second parameter can be a Closure. So instead of writing a named function which is used once and then never again you use a Closure.
IMHO this is the only way to use Closures: as a callback.
Anonymous functions are useful to pass around for later execution or to other code accepting functions. Using them can dramatically reduce the code needed to do things.
Imagine you have a UserCollection object that uses a generic Collection underneath. You want to reduce the UserCollection to just the Users from a certain country. So you could add a method findByCountry() on the UserCollection:
public function findByCountry($country) : UserCollection {
$subset = new UserCollection;
foreach ($this->users as $user) {
if ($user->country === $country) {
$subset->add($user);
}
}
return $subset;
}
This is all fine, but additional finder methods will all do the same: iterate and collect into the subset with only the criteria being different. So a lot of boilerplate code.
You can separate the boilerplate from the criteria easily by adding a method find(callable $callback) on the underlying Collection, like this:
public function find(callable $criteria) : Collection {
$subset = new Collection;
foreach ($this->users as $user) {
if ($criteria($user)) {
$subset->add($user);
}
}
return $subset;
}
This is a generic finder. Now your code in the UserCollection will only contain the actual criteria:
public function findByCountry($country): UserCollection {
return $this->subset(function(User $user) {
return $user->country === $country;
});
}
private function subset($criteria): UserCollection {
return new UserCollection($this->allUsers->find($criteria));
}
By separating the criteria from the boilerplate, it's much easier to grasp that you are trying to find users by country. There is no iteration code distracting from the actual criteria. So it becomes easier to understand and less effort to write. Also, you cannot accidentally mess up the iteration because it's defined elsewhere.
When using anonymous functions like this, they are very similar to using the Strategy Pattern or a FilterIterator. The notable difference being that you are creating them on the fly.
Note that you have to differentiate between Lambdas and Closures. I deliberately ignored the difference for this answer.
I Just heard about Iterables from PHP 7.1 docs.
But didn't got its actual use case and neither the concept is clear for me.
so can anyone explain it with some easy example to grab it faster?
I want to know why and where we use it?
What are the benefits of iterable?
This might help wiki.php.net/rfc/iterable
The main advantage of having iterable is that a class, method or function parameter can be declared iterable but not be concerned of the implementation ie array, Iterator, Generator, etc. So anything that is iterable can be used.
I am a beginner in PHP and for the last 5 hours I have been researching on the subject. As I understand it, an iterator allows us to read large amounts of data that happen to overflow the RAM memory, an example would be an analysis of large log files.
By using a mode called lazy in nature that reads one element at a time, you could read a 4GB file without having to load it all into memory. In theory, you could process an infinite amount of information.
For that we need to define some methods:
<?php
class myIterator implements Iterator {
private $position = 0;
private $array = array("A","B","C",);
public function __construct() {
$this->position = 0;
}
function rewind() {
$this->position = 0;
}
function current() {
return $this->array[$this->position];
}
function key() {
return $this->position;
}
function next() {
$this->position++;
}
function valid() {
return isset($this->array[$this->position]);
}
}
$it = new myIterator;
foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}
In PHP 7.1 this can be reduced to:
<?php
class myIterable {
function foo(iterable $iterable){
foreach ($iterable as $key => $value) {
var_dump($key, $value);
echo "\n";
}
}
}
$it = new myIterable;
$it->foo(array("A","B","C",));
Hope this helps ;)
I'm trying to figure out how to use the output of algorithm as the input of findLarge.
algorithm produces an array that I would like to work on in findLarge
class CSVParser
{
public $output = NULL;
public $digits = NULL;
public $largest = NULL;
public function __construct($file)
{
if (!file_exists($file)) {
throw new Exception("$file does not exist");
}
$this->contents = file_get_contents($file);
$this->output = array();
$this->digits = array();
$this->largest = array();
}
public function algorithm() {....}
public function findLarge($a)
{
// just push it back so I know it's working
var_export($a); // is NULL
$this->largest = $a; // return NULL
}
}
$parser->algorithm();
$parser->findlarge($input); print_r($parser->largest);
It looks like you're just looking for:
$parser->findlarge($parser->algorithm());
You might want to consider, however, a couple of things.
PHP already has str_getcsv
Whether it might be a better idea for algorithm to call findLarge on its own. It looks like this class has two conflicting purposes. One is storing the data, the other is processing it. You might want to think about either having algorithm stateless and having a separate DO, or having algorithm modify the instance's state.
One of the patterns that I frequently run across when developing is trying to collect a column/attribute value from a collection of objects into an array. For example:
$ids = array();
foreach ($documents as $document) {
$ids[] = $document->name;
}
Am I the only one who runs into this? And does PHP have a way to solve this in fewer lines? I've looked but found nothing.
Since I use an MVC framework I have access to a BaseUtil class which contains common functions that don't really fit in any specific classes. One solution proposed by a co-worker is:
class BaseUtil
{
public static function collect($collection, $property) {
$values = array();
foreach ($collection as $item) {
$values[] = $item->{$property};
}
return $values;
}
}
Then I can just do:
$ids = BaseUtil::collect($documents, 'name');
Not too shabby. Anyone else have any other ideas? And am I crazy or does this seem like a problem that PHP should have solved a long time ago?
You can use array_map() function for this purpose:
function getName($obj) {
return $obj->name;
}
$documentsName = array_map("getName", $documents);
You might also consider the create_function() function for lambda functions if you don't want to create a getName() function in the global namespace.
In PHP 5.3 you might even do:
$documentsName = array_map(function ($obj) { return $obj->name; }, $documents);
You can do it easily with ouzo goodies
$names = array_map(Functions::extract()->name, $documents);
or with Arrays (from ouzo goodies)
$names = Arrays::map($documents, Functions::extract()->name);
You can even extract nested fields of methods calls or array access etc:
$names = Arrays::map($documents, Functions::extract()->getAuthor()->roles[0]);
Check out: http://ouzo.readthedocs.org/en/latest/utils/functions.html#extract
See also functional programming with ouzo (I cannot post a link).
another approach is to use "rich" Array objects, like those found in other languages
for example
class Ary extends ArrayObject
{
function pluck($key) {
$a = array();
foreach($this as $sub) $a[] = $sub[$key];
return new self($a);
}
function join($delim = ',') {
return implode($delim, (array) $this);
}
static function init($ary) {
return new self($ary);
}
}
echo
Ary::init(array(
array('foo', 'bar'), array('baz', 'quux')
))->pluck(1)->join();
One of PHP's weaknesses as a language is that it's not terribly expressive.
Where in languages like Ruby or Perl you could probably get this data with a single line of code, you generally need small algorithms like the one you posted to get the desired results.
I'd stick with what you have, but here's another approach just for the heck of it.
class BaseUtil
{
public static function collect($collection, $property)
{
array_walk( $collection, array( __CLASS__, 'reduceObject' ), $property );
return $collection;
}
public static function reduceObject( &$object, $index, $property )
{
$object = $object->{$property};
}
}
Thanks for the input guys. I guess I'm just going to use my coworker's solution:
class BaseUtil
{
public static function collect($collection, $property) {
$values = array();
foreach ($collection as $item) {
$values[] = $item->{$property};
}
return $values;
}
}
Loading a Magento collection, and than looping again in that collection so you can add your desired values into an array it's ineffective. The appropriate way to do this is using getColumnValues() method. This method will give you an array of values by specifying the column name.
Here is the appropriate way of doing this.
$collection =Mage::getModel('your/object')->getCollection()
->addFieldToSelect('customer_id');
$yourArray = $collection->getColumnValues('customer_id');
This will give you an array with all customer_id values you selected.