dpowt82802 2018-03-06 03:46
浏览 64

从db创建CSV的PHP内存限制 - 如何减少PHP使用的内存?

Am running a db query here, and using mysqli with MYSQLI_USE_RESULT in order to execute the query faster. But there is php manipulation of data needed here, so it's not as simple as just writing to a CSV file on each row returned. I know that the database query is finishing (even though it is taking a long time), but the db query does finish, so the problem has to be in the PHP memory side, cause I'm getting a 504 Gateway Timeout Error. My code for this is as follows:

global $wpdb, $root_dir;

if (!defined('ABSPATH'))
    $root_dir = dirname(__FILE__) . '/';
else
    $root_dir = ABSPATH;

require_once($root_dir . 'wp-config.php');
$wp->init();
$wp->parse_request();
$wp->query_posts();
$wp->register_globals();

$start_date = !empty($_GET['start']) ? DateTime::createFromFormat('Y-m-d', $_GET['start']) : '';
$end_date = !empty($_GET['end']) ? DateTime::createFromFormat('Y-m-d', $_GET['end']) : '';

$append = array();

if (!empty($start_date))
    $append[] = $start_date->format('Y-m-d');

if (!empty($end_date))
    $append[] = $end_date->format('Y-m-d');

$filename = 'user-export' . (!empty($append) ? '_' . implode('_', $append) : '');

header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Description: File Transfer');
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=\"" . $filename . ".csv\"");
header("Expires: 0");
header("Pragma: public");


ini_set('memory_limit', '-1');
ini_set('max_execution_time', '-1');
set_time_limit(0);

$headers = array('Order Date/Time', 'Name', 'Billing Address', 'Shipping Address', 'Phone', 'Email Address');
$meta_keys = array('_billing_first_name', '_billing_last_name', '_billing_address_1', '_billing_address_2', '_billing_city', '_billing_state', '_billing_postcode', '_billing_email', '_billing_country', '_billing_phone', '_shipping_address_1', '_shipping_address_2', '_shipping_city', '_shipping_state', '_shipping_postcode', '_shipping_country');
$csv_data = $emails = $order_ids_skipped = $meta = array();

// Using an unbuffered query...
$mysqli  = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

if (mysqli_connect_errno()) {
    printf("Connect failed: %s
", mysqli_connect_error());
    exit();
}

$uresult = $mysqli->query("
    SELECT p.ID, p.post_date, pm.meta_key, pm.meta_value
    FROM {$wpdb->posts} AS p 
    INNER JOIN {$wpdb->postmeta} AS pm ON (pm.post_id = p.ID AND pm.meta_key IN ('" . implode("','", $meta_keys) . "'))
    WHERE p.post_type = 'shop_order' AND p.post_status IN ('wc-sent-delivery', 'wc-awaiting-delivery', 'wc-completed')" . (!empty($start_date) && !empty($end_date) ? " AND (p.post_date >= '" . $start_date->format('Y-m-d') . " 00:00:00' AND p.post_date <= '" . $end_date->format('Y-m-d') . " 23:59:59')" : "") . "
    ORDER BY p.post_date DESC", MYSQLI_USE_RESULT);

if ($uresult) 
{
    while ($order_query = $uresult->fetch_assoc())
    {
        // If the email already exists, continue...
        if ($order_query['meta_key'] == '_billing_email' && !empty($emails) && in_array($order_query['meta_value'], $emails))
        {
            if (isset($csv_data[$order_query['ID']]))
                unset($csv_data[$order_query['ID']]);

            if (isset($meta[$order_query['ID']]))
                unset($meta[$order_query['ID']]);

            $order_ids_skipped[] = $order_query['ID'];

            continue;
        }

        if (in_array($order_query['ID'], $order_ids_skipped)) continue;

        if (!isset($csv_data[$order_query['ID']]))
            $csv_data[$order_query['ID']] = array(
                'order_date' => $order_query['post_date']
            );

        $meta[$order_query['ID']][$order_query['meta_key']] = $order_query['meta_value'];

        if ($order_query['meta_key'] == '_billing_email')
            $emails[] = $order_query['meta_value'];
    }
}
$uresult->close();

if (!empty($meta))
{
    foreach($meta as $order_id => $meta_data)
    {
        $billing = array(
            'addr1' => !empty($meta_data['_billing_address_1']) ? $meta_data['_billing_address_1'] : '',
            'addr2' => !empty($meta_data['_billing_address_2']) ? $meta_data['_billing_address_2'] : '',
            'city' => !empty($meta_data['_billing_city']) ? $meta_data['_billing_city'] : '',
            'state' => !empty($meta_data['_billing_state']) ? $meta_data['_billing_state'] : '',
            'zip' => !empty($meta_data['_billing_postcode']) ? $meta_data['_billing_postcode'] : '',
            'country' => !empty($meta_data['_billing_country']) ? $meta_data['_billing_country'] : ''
        );
        $shipping = array(
            'addr1' => !empty($meta_data['_shipping_address_1']) ? $meta_data['_shipping_address_1'] : '',
            'addr2' => !empty($meta_data['_shipping_address_2']) ? $meta_data['_shipping_address_2'] : '',
            'city' => !empty($meta_data['_shipping_city']) ? $meta_data['_shipping_city'] : '',
            'state' => !empty($meta_data['_shipping_state']) ? $meta_data['_shipping_state'] : '',
            'zip' => !empty($meta_data['_shipping_postcode']) ? $meta_data['_shipping_postcode'] : '',
            'country' => !empty($meta_data['_shipping_country']) ? $meta_data['_shipping_country'] : ''
        );

        $csv_data[$order_id]['name'] = !empty($meta_data['_billing_last_name']) ? trim($meta_data['_billing_first_name']) . ' ' . trim($meta_data['_billing_last_name']) : trim($meta_data['_billing_first_name']);
        $csv_data[$order_id]['billing'] = implode(', ', array_filter($billing));
        $csv_data[$order_id]['shipping'] = implode(', ', array_filter($shipping));
        $csv_data[$order_id]['phone'] = !empty($meta_data['_billing_phone']) ? $meta_data['_billing_phone'] : '';
        $csv_data[$order_id]['email'] = !empty($meta_data['_billing_email']) ? $meta_data['_billing_email'] : '';
    }


    if (!empty($csv_data))
        build_csv_file($csv_data, $headers);
}

// No need to continue here.
exit;

function str_putcsv($input, $delimiter = ',', $enclosure = '"')
{
    // $fp = fopen('php://temp', 'r+b');
    $fp = fopen('php://output', 'r+b');
    fputcsv($fp, $input, $delimiter, $enclosure);
    rewind($fp);
    rtrim(stream_get_contents($fp), "
");
    fclose($fp);
    // return $data;
}

function build_csv_file($data, $headers)
{
    global $root_dir;

    $output = '';

    if (empty($data))
        return array();

    if (!empty($headers))
        str_putcsv($headers, ',', '"') . PHP_EOL;

    foreach($data as $part)
        str_putcsv($part, ',', '"') . PHP_EOL;
}

Saving this file as testing.php and executing the following url like so from the root of wordpress:

https://example.com/testing.php?start=2010-03-05&end=2018-03-05

Just returns a 504 gateway error. This is definitely a PHP Memory Limit problem, however, I thought that a CSV file can be streamed and even using php://output here. Not sure how to better handle this in order to reduce PHP Memory being used, or if there is even a way to do this properly while still maintaining the data correctly.

Can anyone help me please? Very much appreciated. I am using PHP version 7.2 I believe. You'd think that php would be able to handle memory better these days. I believe my PHP Memory is maxed out at 1024MB. I seem to be exceeding the PHP Memory limit all of the time anymore. Is becoming a real nuisance.

  • 写回答

1条回答 默认 最新

  • douzhi9921 2018-03-06 04:41
    关注

    where you're declaring variable $meta

    if (isset($meta[$order_query['ID']]))
    

    also, it would be wise to do a debug by putting exit on each block of statements to know which block of codes consumes that much memory and you can optimize that part only

    评论

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度