Sol*_*son 17 php wordpress json woocommerce
我试图从数据库查询的大转储创建一个JSON文件,并在我将LIMIT设置为返回100000行时工作,但是当我想要返回所有行时,它只会转到502错误(页面请求)被取消,因为它花了太长时间才完成).想知道是否有一种方法可以使用php简化JSON文件的创建,或者如果有一个库可以让我在部分中构建json文件?
基本上我在这里运行.php文件试图从woocommerce获取json格式的所有订单,因为我购买的"CSV Import Suite"插件在导入订单时不起作用,它只是留在队列中.
所以,我决定尝试自己导出所有订单,但是继续点击502错误页面,它也从不创建.json文件,所以我想我需要一种方法以某种方式流式传输.任何有关这方面的帮助将不胜感激......
ini_set('memory_limit', '-1');
ini_set('max_execution_time', '-1');
set_time_limit(0);
error_reporting(E_ALL);
ob_implicit_flush(TRUE);
ob_end_flush();
global $wpdb, $root_dir;
if (!defined('ABSPATH'))
$root_dir = dirname(__FILE__) . '/';
else
$root_dir = ABSPATH;
$download = isset($_GET['download']);
// Allows us to use WP functions in a .php file without 404 headers!
require_once($root_dir . 'wp-config.php');
$wp->init();
$wp->parse_request();
$wp->query_posts();
$wp->register_globals();
if (empty($download))
$wp->send_headers();
// exclude
$exclude_post_statuses = array('trash', 'wc-refunded', 'wc_cancelled');
$start_date = !empty($_GET['start_date']) ? DateTime::createFromFormat('Y-m-d', $_GET['start_date']) : '';
$end_date = !empty($_GET['end_date']) ? DateTime::createFromFormat('Y-m-d', $_GET['end_date']) : '';
$order_db = array(
'columns' => array(
'p' => array('ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_excerpt', 'post_status', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_content_filtered', 'post_parent', 'guid', 'menu_order', 'post_type', 'post_mime_type', 'comment_count'),
'pm' => array('meta_id', 'post_id', 'meta_key', 'meta_value'),
'oi' => array('order_item_id', 'order_item_name', 'order_item_type', 'order_id'),
'oim' => array('meta_id', 'order_item_id', 'meta_key', 'meta_value')
)
);
$select_data = '';
$total_columns = count($order_db['columns']);
$i = 1;
foreach($order_db['columns'] as $column_key => $columns)
{
$select_data .= implode(', ', array_map(
function ($v, $k) { return $k . '.' . $v . ' AS ' . $k . '_' . $v; },
$columns,
array_fill(0, count($columns), $column_key)
));
if ($i < $total_columns)
$select_data .= ', ';
$i++;
}
// HUGE DATABASE DUMP HERE, needs to be converted to JSON, after getting all columns of all tables...
$orders_query = $wpdb->get_results('
SELECT ' . $select_data . '
FROM ' . $wpdb->posts . ' AS p
INNER JOIN ' . $wpdb->postmeta . ' AS pm ON (pm.post_id = p.ID)
LEFT JOIN ' . $wpdb->prefix . 'woocommerce_order_items AS oi ON (oi.order_id = p.ID)
LEFT JOIN ' . $wpdb->prefix . 'woocommerce_order_itemmeta AS oim ON (oim.order_item_id = oi.order_item_id)
WHERE p.post_type = "shop_order"' . (!empty($exclude_post_statuses) ? ' AND p.post_status NOT IN ("' . implode('","', $exclude_post_statuses) . '")' : '') . (!empty($start_date) ? ' AND post_date >= "' . $start_date->format('Y-m-d H:i:s') . '"' : '') . (!empty($end_date) ? ' AND post_date <= "' . $end_date->format('Y-m-d H:i:s') . '"' : '') . '
ORDER BY p.ID ASC', ARRAY_A);
$json = array();
if (!empty($orders_query))
{
foreach($orders_query as $order_query)
{
if (!isset($json[$order_query['p_post_type']], $json[$order_query['p_post_type']][$order_query['p_post_name']]))
$json[$order_query['p_post_type']][$order_query['p_post_name']] = array(
'posts' => array(),
'postmeta' => array(),
'woocommerce_order_items' => array(),
'woocommerce_order_itemmeta' => array()
);
if (!empty($order_query['p_ID']))
$json[$order_query['p_post_type']][$order_query['p_post_name']]['posts'][$order_query['p_ID']] = array_filter($order_query, function($k) {
$is_p = strpos($k, 'p_');
return $is_p !== FALSE && empty($is_p);
}, ARRAY_FILTER_USE_KEY);
if (!empty($order_query['pm_meta_id']))
$json[$order_query['p_post_type']][$order_query['p_post_name']]['postmeta'][$order_query['pm_meta_id']] = array_filter($order_query, function($k) {
$is_pm = strpos($k, 'pm_');
return $is_pm !== FALSE && empty($is_pm);
}, ARRAY_FILTER_USE_KEY);
if (!empty($order_query['oi_order_item_id']))
$json[$order_query['p_post_type']][$order_query['p_post_name']]['woocommerce_order_items'][$order_query['oi_order_item_id']] = array_filter($order_query, function($k) {
$is_io = strpos($k, 'oi_');
return $is_io !== FALSE && empty($is_io);
}, ARRAY_FILTER_USE_KEY);
if (!empty($order_query['oim_meta_id']))
$json[$order_query['p_post_type']][$order_query['p_post_name']]['woocommerce_order_itemmeta'][$order_query['oim_meta_id']] = array_filter($order_query, function($k) {
$is_oim = strpos($k, 'oim_');
return $is_oim !== FALSE && empty($is_oim);
}, ARRAY_FILTER_USE_KEY);
}
}
// Downloading or viewing?
if (!empty($download))
{
// Outputs json in a textarea for you to copy and paste into a .json file for import...
if (!empty($json))
{
$filename = uniqid('orders_') . '.json';
$fp = fopen($filename, 'w');
fwrite($fp, json_encode($json));
fclose($fp);
$size = filesize($root_dir . '/' . $filename);
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename=\"" . $filename . "\"");
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $size);
readfile($root_dir . '/' . $filename);
}
}
else
{
// Outputs json in a textarea for you to copy and paste into a .json file for import...
if (!empty($json))
echo '<textarea cols="200" rows="50">', json_encode($json), '</textarea>';
}
Run Code Online (Sandbox Code Playgroud)
创建的JSON文件可能超过500 MB,甚至可能高达1 Gig的数据.所以,我相信PHP在这里耗尽内存,并且需要以某种方式逐位处理,无论是在后台还是完全,而不会达到php内存限制.我相信内存限制设置为1024 MB,这是非常高,但不够高和tbh,对于我正在做的事情,我认为我们没有足够的内存来执行操作.我需要改变处理json和/或下载它的方式.而且我不想创建多个json文件,请只使用1个JSON文件.
我想可能会有几个问题.首先,我建议你做一些分析.
// HUGE DATABASE DUMP HERE, needs to be converted to JSON, after getting all columns of all tables...
echo 'Start Time: '. date("Y-m-d H:i:s");
echo ' Memory Usage: ' . (memory_get_usage()/1048576) . ' MB \n';
$orders_query = $wpdb->get_results('
SELECT ' . $select_data . '
FROM ' . $wpdb->posts . ' AS p
INNER JOIN ' . $wpdb->postmeta . ' AS pm ON (pm.post_id = p.ID)
LEFT JOIN ' . $wpdb->prefix . 'woocommerce_order_items AS oi ON (oi.order_id = p.ID)
LEFT JOIN ' . $wpdb->prefix . 'woocommerce_order_itemmeta AS oim ON (oim.order_item_id = oi.order_item_id)
WHERE p.post_type = "shop_order"' . (!empty($exclude_post_statuses) ? ' AND p.post_status NOT IN ("' . implode('","', $exclude_post_statuses) . '")' : '') . (!empty($start_date) ? ' AND post_date >= "' . $start_date->format('Y-m-d H:i:s') . '"' : '') . (!empty($end_date) ? ' AND post_date <= "' . $end_date->format('Y-m-d H:i:s') . '"' : '') . '
ORDER BY p.ID ASC', ARRAY_A);
echo 'End Time: '. date("Y-m-d H:i:s");
echo ' Memory Usage: ' . (memory_get_usage()/1048576) . ' MB \n';
die('Finished');
$json = array();
Run Code Online (Sandbox Code Playgroud)
以上内容将帮助您了解正在使用的内存量.如果它在打印'完成'之前失败,我们知道它不是json问题.如果脚本运行正常,那么我们可以先创建一个csv文件而不是json.由于您正在运行select查询(此时),因此它不必是您需要的嵌套json文件.只需创建一个CSV文件即可实现平面结构.
$csvFile = uniqid('orders') . '.csv';
$fp = fopen($csvFile, 'w');
if (!empty($orders_query))
{
$firstRow = true;
foreach($orders_query as $order_query)
{
if(true === $firstRow) {
$keys = array_keys($order_query);
fputcsv($fp, $order_query);
$firstRow = false;
}
fputcsv($fp, $order_query);
}
}
fclose($fp);
Run Code Online (Sandbox Code Playgroud)
如果以上工作正常,至少有一个csv文件可以使用.
此时我不确定您的数据结构嵌套有多复杂.例如,您有'p_post_type'和'p_post_name'存在多少个不同的值.您可能需要解析csv文件并为每个['p_post_type'] ['p_post_name'] ['posts'],['p_post_type'] ['p_post_name'] ['posts'],['p_post_type创建多个json文件''['p_post_name'] ['woocommerce_order_items']和['p_post_type'] ['p_post_name'] ['woocommerce_order_itemmeta'].
如果文件数量很少,您可以编写脚本以自动合并它们或手动执行.如果您有太多嵌套项,可能创建的json文件数量可能很多,可能很难合并它们,可能不是一个可行的选项.
如果json文件的数量很多,我想知道拥有如此庞大的单个json文件的目的是什么.如果导出是一个问题,导入也可能是一个问题,尤其是在内存中摄取如此庞大的json文件.我相信如果创建json文件的目的是以某种形式导入它,在将来的某个阶段,我想你可能不得不看一下只有一个csv文件的选项,你用来过滤掉任何东西在那个时间点需要.
我希望这有帮助.
进一步更新
在我看来,$ wpdb-> get_results正在使用mysqli_query/mysql_query(取决于您的配置)来获取结果.请参阅wordpress查询文档.以这种方式获取数据不是内存有效的方法.我相信你可能会失败($ wpdb-> get_results)本身.我建议你不使用$ wpdb运行查询.每当需要大数据检索时,都存在无缓冲查询的概念,这对内存的影响非常小.更多信息可以在这里找到mysql unbuffering.
即使你已经超越了这一点,你仍然会遇到内存问题,因为你在$ json变量中存储所有内容的方式会占用大量的内存.$ json是一个数组,知道PHP数组如何工作会很有趣.PHP数组是动态的,并且每次添加新元素时它们都不会分配额外的内存,因为这样会非常慢.相反,它将数组大小增加到2的幂,这意味着每当限制耗尽时,它将数组限制增加到其当前限制的两倍,并且在此过程中尝试将内存增加到限制的两倍.这对PHP 7来说不是一个问题,因为它们已经对php核心做了一些重大改动.因此,如果您有2GB数据可能需要存储在$ json中,则脚本可以轻松地在3-4 GB内存之间分配,具体取决于它何时达到限制.更多细节可以在这里找到PHP数组和PHP内存如何实际工作
如果你考虑$ orders_query的开销,这是一个数组加上$ json的开销,由于PHP数组的工作方式,它是相当可观的.
您还可以尝试创建另一个数据库B.因此,当您从数据库A读取数据时,您同时开始将数据写入数据库B.最后,您将拥有数据库B,其中包含MySQL的所有数据.您也可以将相同的数据推送到一个MongoDB,它可以快速闪电,并可能帮助您使用json嵌套.MongoDB旨在高效地处理大型数据集.
JSON流媒体解决方案
首先,我想说流式传输是顺序/线性过程.因此,它没有记录在此时间点之前添加的内容或在此时间点之后添加的内容.它适用于小块,这就是它如此高效的内存.所以当你真正写或读时,责任在于脚本,它维护一个特定的顺序,这就是说你正在写/读你自己的json,因为流只能理解文本而且不知道json是什么和写作/阅读正确的文章不会打扰自己.
我在github上找到了一个库https://github.com/skolodyazhnyy/json-stream,它可以帮助你实现你想要的.我已经尝试了代码,我可以看到它会在你的代码中进行一些调整.
我要为你写一些伪代码.
//order is important in this query as streaming would require to maintain a proper order.
$query1 = select distinct p_post_type from ...YOUR QUERY... order by p_post_type;
$result1 = based on $query1;
$filename = 'data.json';
$fh = fopen($filename, "w");
$writer = new Writer($fh);
$writer->enter(Writer::TYPE_OBJECT);
foreach($result1 as $fields1) {
$posttype = $fields1['p_post_type'];
$writer->enter($posttype, Writer::TYPE_ARRAY);
$query2 = select distinct p_post_name from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype order by p_post_type,p_post_name;
$result2 = based on $query2;
foreach($result2 as $fields2) {
$postname = $fields1['p_post_name'];
$writer->enter($postname, Writer::TYPE_ARRAY);
$query3 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where p_ID is not null order by p_ID;
$result3 = based on $query3;
foreach($result2 as $field3) {
$writer->enter('posts', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field3);
}
$writer->leave();
$query4 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where pm_meta_id is not null order by pm_meta_id;
$result4 = based on $query4;
foreach($result4 as $field4) {
$writer->enter('postmeta', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field4);
}
$writer->leave();
$query5 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where oi_order_item_id is not null order by oi_order_item_id;
$result5 = based on $query5;
foreach($result5 as $field5) {
$writer->enter('woocommerce_order_items', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field5);
}
$writer->leave();
$query6 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where oim_meta_id is not null order by oim_meta_id;
$result6 = based on $query6;
foreach($result6 as $field6) {
$writer->enter('woocommerce_order_itemmeta', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field5);
}
$writer->leave();
}
$writer->leave();
fclose($fh);
Run Code Online (Sandbox Code Playgroud)
您可能必须开始将查询限制为10个,直到您做对了.因为上面的代码可能不仅仅是按原样运行.您应该能够以类似的方式阅读代码,因为同一个库有一个Reader类可以提供帮助.我测试了读者和作者,他们似乎工作得很好.
首先,你应该问自己一个问题:我需要自己编写数据库转储吗?
如果没有,那么您可以简单地使用一些可以为您完成工作的服务。mysqldump-php应该能够完成这项工作。
然后你可以简单地:
include_once(dirname(__FILE__) . '/mysqldump-php-2.0.0/src/Ifsnop/Mysqldump/Mysqldump.php');
$dump = new Ifsnop\Mysqldump\Mysqldump('mysql:host=localhost;dbname=testdb', 'username', 'password');
$dump->start('storage/work/dump.sql');
Run Code Online (Sandbox Code Playgroud)
这应该创建.sql文件。但是,您想要json文件。但这应该不是问题。该工具将完成其余的工作:http://www.csvjson.com/sql2json
您还可以在github上找到源代码sql2json: https: //github.com/martindrapeau/csvjson-app