How to speed up this method? Is is_int that slow? - php

I am working with a (highly optimized/adapted version of) CakePHP 2.3 and my application is running on VERY slow hardware (300MHz ARM) so I am still optimizing wherever I can. One method of the framework is called VERY often and not very fast (~1-5ms), but I can not think of a way to improve it (without changing the output) - in total I spend ~10% of the total time in this method:
public static function normalizeObjectArray($objects) {
$normal = array();
foreach ($objects as $i => $objectName) {
$options = array();
if (!is_int($i)) {
$options = (array)$objectName;
$objectName = $i;
}
list(, $name) = pluginSplit($objectName);
$normal[$name] = array('class' => $objectName, 'settings' => $options);
}
return $normal;
}
Does anyone have an idea how to speed this up?
The profiler has the following output for one of the calls - I already asked how to improve pluginSplit in this question:
(Profiling is about 10-15 times slower then normal execution)
Is it the is_int that is that slow or where is that time "lost"?

Optimize by removing the method.
normalizeObjectArray is the method that converts arrays like this:
public $foo = array(
'One',
'Two',
'Three' => array('option' => 1, 'other' => 2)
);
into:
public $foo = array(
'One' => array('className' => 'One', 'settings' => array()),
'Two' => array('className' => 'Two', 'settings' => array()),
'Three' => array('className' => 'Three', 'settings' => array('option' => 1, 'other' => 2))
);
If instead of trying to optimize this code, you refactor the code to not call it and ensure that wherever it's called the array is already in the format required (e.g. component, helper, behavior arrays), the logic is redundant and can simply be removed.

First you can avoid the list. Instead, you can do:
$normal[pluginSplit($objectName)[1]] = ... ;
Secondly, I think (not sure) that ctype_digit() can improve performance a bit.
By the way, could you give an example of content of $objects? It sounds like a weird array...

Related

array_diff_assoc() or foreach()? Which is faster?

I have two arrays, for example $session and $post with 100+ values. I will compare the $post array values with $session array. If post is different then it will be taken to result array else not.
We can try this using array_diff_assoc($post, $session) and foreach(). Which one is faster?
For profiling, Phil has suggested a great way in his reply, but I will link it here too, just in case:
Simplest way to profile a PHP script
Practically, you need to know what each approach does. in array_diff_assoc, you are returning the difference between 2 collections, after comparing the key/value couples for each element. It will then return an array that contains the entries from array1 that are not present in array2 or array3, etc.
In a for each loop, you will need to hard code the same function (assuming that's what you need). You will need to take the first element, then look for the combination in your other arrays. If it matches your requirements, you will save it into your output array, or even print it directly.
Same principles apply, but then again, it will be up to profiling to determine the faster approach. Try doing so on a large number of big arrays, as the difference isn't noticeable at smaller scales.
I'll leave this as a stub/example, please edit, or use for profiling.
<?php
$before = [
'name' => 'Bertie',
'age' => '23'
];
$after = [
'name' => 'Harold',
'age' => '23',
'occupation' => 'Bus driver'
];
function changed_1($after, $before) {
return array_diff_assoc($after, $before);
}
function changed_2($after, $before) {
$changed = [];
foreach($after as $k => $v) {
if(isset($before[$k]) && $before[$k] !== $v)
$changed[$k] = $v;
if(!isset($before[$k]))
$changed[$k] = $v;
}
return $changed;
}
var_export(changed_1($after, $before));
var_export(changed_2($after, $before));
Output:
array (
'name' => 'Harold',
'occupation' => 'Bus driver',
)array (
'name' => 'Harold',
'occupation' => 'Bus driver',
)

How to make PhpStorm suggest / autocomplete array keys

<?php
function test(){
return array(
'one' => 1,
'two' => 2
);
}
$test = test();
echo $test[''];
I would like to put my cursor in the last line in between the single quotes, and have it suggest one or two. How can I do this?
The below works fine, so why doesn't it work inside of a function:
$test = array(
'one' => 1,
'two' => 2
);
echo $test[''];
// Suggests 'one' and 'two'
The below works fine, so why doesn't it work inside of a function:
Because it's implemented only for variables/class properties.
How can I do this?
Just install deep-assoc-completion plugin. It can do even more than that (e.g. help with completion of array parameter -- what keys are possible (if you bother to describe those keys, of course) etc).

PHP - MongoDB move array to subarray

Ok I am making a blog system using MongoDB as the back end. I want to do the same thing as wordpress when you edit it saves the past versions and allows you to revert to them if needed.
I would like to do the same.
I have a few ways to doing it. but un sure if this is the best easiest way to do it and would like some suggestions.
the first is a find and the insert $SET
<?php
$cursor = $collection->find(array("_id"=> new MongoId($data)));
if ($cursor->count() > 0)
{
while( $cursor->hasNext() ) {
foreach($cursor->getNext() as $key => $value)
{
define("_".strtoupper($key), $value);
}
}
$cursor = $collection->update(array("_id" => new MongoId($data)),
'$set'=>array("title"=>$data['TITLE'], "content"=>$data['content'], "past_versons"=>array("title" => _TITLE, "content" => _CONTENT)));
}
?>
So my question is this the way I would do it.
here a sample JSON
{
"title":"blog title",
"content":"blog content",
"past_verson":[{"title":"blog title past","content":"past blog content"}]
}
In your example, you're iterating over a MongoCursor even though you only expect to work with one document at most. MongoCollection::findOne() is going to be more appropriate here, as it will return the document array of the first result or null if no documents matched.
Even if you needed to iterate across multiple results, you'd do better to take advantage of the iterable nature of MongoCursor (it implements Iterator, which extends Traversable, which means you can use it in a foreach loop). I also don't think define() is appropriate for storing what is essentially a temporary variable here. Moreover, suppose this code needed to run twice (error due to redefinition) or the title or content was not a scalar (another error).
Consider the following rewrite (I assumed $data['id'] is correct and you made a typo in the above code by using $data alone for the MongoId):
$document = $collection->findOne(array('_id' => new MongoId($data['id'])));
if (null !== $document) {
$collection->update(
array('_id' => new MongoId($data['id'])),
array(
'$set' => array(
'title' => $data['title'],
'content' => $data['content'],
),
'$push' => array(
'past_versions' => array(
'title' => $document['title'],
'content' => $document['content'],
),
),
)
);
}
One deviation here, which you may not need, is that I'm aggregating old versions into a past_versions array. We can use the $push operator to append to an array field in the document. Alternatively, you could simply use $set if you only need/want to store the most recent version.

PHP Lithium: Filtering an existent DocumentSet and get first Match

I am retrieving a DocumentSet in Lithium from MongoDB, but I don't want to process the documents all at once. Instead I would like to have a filter, which I just could tell something like this:
$manyDocuments->giveMeTheOneWhere(array('foo' => 'bar'));
I already tried to do it this way, but it didn't work:
$manyDocuments->find(function($singleDocument){
return ($singleDocument->foo == 'bar');
});
Even if I manually return true inside the closure, it always returns an empty DocumentSet.
Just to add clarity: I am not looking for a database-operation, instead I want to get one out of an already existent DocumentSet. Is there a fancy way to achieve this or do I need to iterate through the set using a custom function?
That looks right to me. Is that the exact code you are using?
For example, is the 'bar' value you are using something you are passing in?
I'm on the latest of the master branch of Lithium and wrote this unit test which works for me. I'm not really sure why you're getting an empty DocumentSet.
$docs = new DocumentSet(array('data' => array(
new Document(array('data' => array(
'id' => 1,
'foo' => 'bar'
))),
new Document(array('data' => array(
'id' => 2,
'foo' => 'baz'
))),
new Document(array('data' => array(
'id' => 3,
'foo' => 'bar'
))),
new Document(array('data' => array(
'id' => 4,
'blah' => 'ack'
)))
)));
$filtered = $docs->find(function($doc) {
return $doc->foo === 'bar';
});
$expected = array(
'0' => array('id' => 1, 'foo' => 'bar'),
'2' => array('id' => 3, 'foo' => 'bar')
);
$this->assertIdentical($expected, $filtered->data());
Instead of using find() I just used first() with a closure. This works as expected. Sadly that was the only thing I didn't try before. Excuse me for answering my own question.
Anyway I'd still be interested in a way to get another Document Set.

Arrays vs Single vars

I recently faced a design problem with PHP. I noticed that in a function you can pass as parameter an array. I didn't noticed the powerful of this thing first, but now i'm obsessed with arrays.
For example, in my template class i have to pass some variables and some mysqli_results into the template file (like phpbb do). And i was wondering which one of the following possibilities is the best.
# 1
$tpl = new template(array(
'vars' = array('var1' => 'val1', 'var2' => 'val2'),
'loops' = array('loop1' => $result1, 'loop2' => $result2)
));
# 2
$tpl = new template;
$tpl->assignVars(array(
'var1' => 'val1',
'var2' => 'val2'
));
$tpl->assignloops(array(
'loop1' => $result1,
'loop2' => $result2
));
# 3
$tpl = new template;
$tpl->assignVar('var1', 'val1');
$tpl->assignVar('var1', 'val1');
$tpl->assignLoop('loop1', $result1);
$tpl->assignLoop('loop2', $result2);
Or if there is something better. I was even thinking about creating a db class that performs a query as follow:
$result = $db->fastQuery(array(
'select' => 'user-name',
'from' => $table,
'where' => array('user-id' => 123, 'user-image' => 'none'),
'fetch' => true
));
Oh my God, i'm really obsessed.
If it was up to me I would chose #1, I don't like nesting objects and arrays only if it is necessary. by doing so I keep my code simple.
and if you follow your obsession you may end up writing a full ORM.
#4
Allowing both:
function assign($name, $val = null)
{
if (is_array($name)) {
// loop through and assign
} else {
// assign single var
}
}
This is akin to overloading techniques you would see in C++/Java.
You can also allow #1 by just calling assign in the constructor. It is not uncommon in OOP programming to have the constructor allow a shortcut to setting properties that can also be set in other methods.

Categories