0

我基本上是从各种 API 获取数据并使用 PHP 将它们组合在一起 - 就像网络混搭一样。我目前正在使用 4 个 foreeach 语句将收集到的数据插入到它们各自的数组中。我认为当前代码效率低下,因为加载显示 PHP 数据的页面可能需要大约 3 秒。在过去,我只有一个大的 foreach 循环来一次遍历所有数据并打印它们。但这对我来说也感觉效率低下。

那么我怎样才能让我的代码在处理速度更快方面更有效率呢?我见过一些混搭网站,例如Soundeer,加载时间大约为一秒钟。那是因为他们的代码效率吗?

我正在使用的代码是:

$echonest_uri = simplexml_load_file("http://developer.echonest.com/api/v4/artist/search?api_key=$APIkey&style=rap&results=10&start=$element_num&bucket=id:deezer&bucket=images&sort=familiarity-desc&format=xml");

//Swap the comments for when in UWE or not
//$echonest_xml =  new SimpleXMLElement($echonest_uri);
$echonest_xml = $echonest_uri;

$artist_name = array();
$artist_image = array();
$echonest_id = array();
$full_deezer_id = array();
$deezer_id = array();
$num_of_albums = array();

//Loop through each entries in the id_arr and make each image of the artist a link to the album page passing all the relevant information. 
foreach($echonest_xml->artists->artist as $artist){
    $artist_name[] = $artist->name;
    $artist_image[] = $artist->images->image[0]->url;
    $echonest_id[] = $artist->id;
    $full_deezer_id[] = $artist->foreign_ids->foreign_id->foreign_id;
}

foreach($full_deezer_id as $key => $value){
    preg_match('#deezer:artist:([A-Z,a-z,0-9]+)#', $value, $id);
    $deezer_id[] = (string)$id[1];
}

foreach($deezer_id as $id_index => $id){
    $deezer_xml = simplexml_load_file("http://api.deezer.com/2.0/artist/$id/albums&output=xml");
    $num_of_albums[] = $deezer_xml->total;
}

//The variable which will contain the HTML code to display the artists.
$output = null;


foreach($deezer_id as $key => $value){

    $fav_count_query = "SELECT COUNT(user_index) FROM fav_artist WHERE artist_deezer_id = '$value'";
    $fav_count_resource = $mysqli->query($fav_count_query);
    $fav_count = $fav_count_resource->fetch_assoc(); 

    $output .=  <<<HERE
                <div class="artist-container">
                    <a href="albums.php?echonest_id={$echonest_id[$key]}&deezer_id={$deezer_id[$key]}&artist_name={$artist_name[$key]}&artist_image={$artist_image[$key]}&num_of_albums={$num_of_albums[$key]}" class="artist-image">
                        <img src="{$artist_image[$key]}" alt="{$artist_name[$key]}" title="{$artist_name[$key]}"/>
                    </a>

                    <a href="albums.php?echonest_id={$echonest_id[$key]}&deezer_id={$deezer_id[$key]}&artist_name={$artist_name[$key]}&artist_image={$artist_image[$key]}&num_of_albums={$num_of_albums[$key]}" class="artist-name">
                        {$artist_name[$key]}
                    </a>
                    <a href="albums.php?echonest_id={$echonest_id[$key]}&deezer_id={$deezer_id[$key]}&artist_name={$artist_name[$key]}&artist_image={$artist_image[$key]}" class="album-number">Albums: 
                        {$num_of_albums[$key]}
                    </a>
                </div>
HERE;

}
4

2 回答 2

3

由于多个 foreach 循环,您的代码很可能并不慢(老实说,在现实场景中您不会感觉到差异)。在这里伤害你的是从外部站点下载一些东西。

我的解决方案是每 5 分钟自动下载一次(通过 cronjob 或仅在用户访问页面时),如果不到 5 分钟前,则显示放置在服务器上的缓存版本。

也就是说,这似乎是一个搜索,所以结果很可能不会有太大变化。也许有一个 1 天的缓存代替?(每个新的搜索查询仍然需要 3 秒,但是如果您希望许多用户进行相同的查询,那么这将使您的网站看起来更快)。

一种简单的方法是使用该函数来测试您的代码在哪里出现了时间错误。microtime我通常做的是这样的:

$beforeCall = microtime(true);
$echonest_uri = simplexml_load_file("http://developer.echonest.com/api/v4/artist/search?api_key=$APIkey&style=rap&results=10&start=$element_num&bucket=id:deezer&bucket=images&sort=familiarity-desc&format=xml");
echo "The call took " . number_format(microtime(true) - $beforeCall, 4) . " seconds";

如果您使用的是 XML(看起来像),您可以使用启用缓存的SimplePie。一篇很好的文章是SimplePie 自己关于缓存系统的文章,您可以在其中调整缓存持续时间set_cache_duration

于 2013-01-19T20:20:11.987 回答
2

缓存

最相关的是,您缺少的是缓存。缓存 api 调用结果的简单实现是:

$url = "http://developer.echonest.com/api/v4/artist/search?api_key=$APIkey&style=rap&results=10&start=$element_num&bucket=id:deezer&bucket=images&sort=familiarity-desc&format=xml";
$cacheFile = md5($url) . '.xml';

// If the cache file exists and is less than an hour old - use it.
if (file_exists($cacheFile) && filemtime($cacheFile) > (time() - 3600)) {
    $xml = file_get_contents($cacheFile);
// else download it
} else {
    $xml = file_get_contents($url);
    file_put_contents($cacheFile, $xml);
}
$xml = simplexml_load_file($cacheFile);

即不要在每个请求上谈论 api - 与它交谈一次,并使用响应,只要您认为它适当/安全认为有效(在上面的示例中为 3600 秒 - 或一小时)。

这种逻辑最好封装在一个方法中。

DRY 代码执行

与缓存相比不太重要,问题中的代码是 4 个相互馈送的 foreach 循环 - 这不是编写此类逻辑的有效方法。应用不要重复执行代码是一个好主意,即:

foreach($echonest_xml->artists->artist as $artist){
    ...
    $full_deezer_id[] = $artist->foreign_ids->foreign_id->foreign_id;
}

foreach($full_deezer_id as $key => $value){ // <--
    ...
    $deezer_id[] = (string)$id[1];
}

foreach($deezer_id as $id_index => $id){ // <--
    ...
}

foreach($deezer_id as $key => $value){ // <--

可以更好地写为

foreach($echonest_xml->artists->artist as $artist){
    processArtist($artist);
}

foreach($buidThis as $row) {

因此,编写代码最多循环两次(但最好只循环一次),而不是在同一数据上循环四次。

仅使用问题中的逻辑,例如可以轻松组合前两个循环:

foreach($echonest_xml->artists->artist as $artist){
    preg_match('#deezer:artist:([A-Z,a-z,0-9]+)#', $artist->foreign_ids->foreign_id->foreign_id, $id);
    $deezer_id[] = (string)$id[1];
}

应该对所有代码应用相同的分析:尝试限制迭代/递归逻辑的数量。

于 2013-01-19T20:27:29.087 回答