I made a plugin that basically reads a CSV file and imports data to relevant tables.
However, the action seems to create an error:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65015808 bytes) in /var/www/proj/wp-includes/functions.php on line
which led me to this code in functions.php:
function wp_ob_end_flush_all() {
$levels = ob_get_level();
for ($i=0; $i<$levels; $i++)
ob_end_flush();
}
I did a Google and came across two popular solutions, both of which didn't seem to work.
Solution 1: disabling zlib - this is already disabled.
Solution 2:remove_action('shutdown', 'wp_ob_end_flush_all', 1);
Solution 2 still errors but with no message, which, isn't exactly ideal.
This is the script that's causing the error:
<?php
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
error_reporting(-1);
# load core wp fnc
require_once $_SERVER['DOCUMENT_ROOT']. '/wp-load.php';
# load db functions
require_once $_SERVER['DOCUMENT_ROOT']. '/wp-admin/includes/upgrade.php';
# load admin fnc
require_once $_SERVER['DOCUMENT_ROOT']. '/wp-content/plugins/vendor-module/admin/inc/admin-functions.php';
global $wpdb;
$admin_functions = new vendor_module_admin_functions();
# get csv
$file = $_FILES['csv'];
$name = $file['name'];
$dir = $file['tmp_name'];
# rm spaces, replace with _
$name = str_replace(' ', '_', $name);
$file_location = $_SERVER['DOCUMENT_ROOT']. '/wp-content/plugins/vendor-module/uploads/import/'. $name;
# if successfully moved, carry on, else return
$moved = ($file['error'] == 0 ? true : false);
$error = false;
if (!$moved) {
echo 'Error! CSV file may be incorrectly formatted or there was an issue in reading the file. Please try again.';
} else {
move_uploaded_file($dir, $file_location);
$id = $_POST['val'];
$type = $_POST['type'];
$table = ($type == 1 ? 'vendor_module_type_one' : 'vendor_module_type_two');
$handle = fopen($file_location, 'r');
$parts = array();
$components = array();
$i = 0;
while (($data = fgetcsv($handle, 1000, ',')) !== false)
{
if (is_array($data)) {
if (empty($data[0])) {
echo 'Error! Reference can\'t be empty. Please ensure all rows are using a ref no.';
$error = true;
continue;
}
# get data
$get_for_sql = 'SELECT `id` FROM `'. $wpdb->prefix. $table .'` WHERE `ref` = %s';
$get_for_res = $wpdb->get_results($wpdb->prepare($get_for_sql, array($data[0])));
if (count($get_for_res) <= 0) {
echo 'Error! Reference has no match. Please ensure the CSV is using the correct ref no.';
$error = true;
exit();
}
$get_for_id = $get_for_res[0]->id;
# create data arrays
$parts[$i]['name'] = $data[1];
$parts[$i]['ref'] = $data[2];
$parts[$i]['for'] = $get_for_id;
$components[$i]['part_ref'] = $data[2];
$components[$i]['component_ref'] = $data[3];
$components[$i]['sku'] = $data[4];
$components[$i]['desc'] = utf8_decode($data[5]);
$components[$i]['req'] = $data[6];
$components[$i]['price'] = $data[7];
unset($get_for_id);
unset($get_for_res);
unset($get_for_sql);
$i++;
}
}
fclose($handle);
unlink($file_location);
# get unique parts only
$parts = array_unique($parts, SORT_REGULAR);
# check to see if part already exists, if so delete
$search_field = ($type == 1 ? 'id_field_one' : 'id_field_two');
$check_sql = 'SELECT `id` FROM `'. $wpdb->prefix .'vendor_module_parts` WHERE `'. $search_field .'` = %d';
$delete_parts_sql = 'DELETE FROM `'. $wpdb->prefix .'vendor_module_parts` WHERE `'. $search_field .'` = %d';
$delete_components_sql = 'DELETE FROM `'. $wpdb->prefix .'vendor_module_components` WHERE `part_id` = %d';
$check_res = $wpdb->get_results($wpdb->prepare($check_sql, array($id)));
if ($check_res) {
$wpdb->query($wpdb->prepare($delete_parts_sql, array($id)));
}
$part_ids = $admin_functions->insert_parts($parts, $type);
unset($parts);
unset($delete_parts_sql);
unset($search_field);
unset($check_sql);
unset($check_res);
unset($i);
# should never be empty, but just as a precaution ...
if (!empty($part_ids)) {
foreach ($components as $key => $component)
{
$components[$key]['part_id'] = $part_ids[$component['part_ref']];
}
# rm components from assoc part id
foreach ($part_ids as $id)
{
$wpdb->query($wpdb->prepare($delete_components_sql, array($id)));
}
# insert components
$admin_functions->insert_components($components);
} else {
echo 'Error!';
}
echo (!$error ? 'Success! File Successfully Imported.' : 'There be something wrong with the import. Please try again.');
}
it's triggered through a button press and uses AJAX to handle it etc.
I'm not sure why a memory leak is occurring or why WordPress doesn't offer more useful error messages. I don't call that function.. so I'm guessing it's something WordPress is doing in the background when things are run.
My info:
PHP 7.2.10
Apache 2.4
Linux Mint 19
Also happens on my server:
PHP 7.1.25
Apache 2.4
CentOS 7.6.1810
WordPress running version: 4.9.8
.click
which points to this page ... never really done WP development - more of a Symfony/Magento dev - used to this way, so just assumed it would be ok haharequire
some WP files. This is not the WordPress way, the documentation describes how to do AJAX or using the more modern REST API. I haven't looked too deep in your code but the thing is: you break WP flow by including some files, so it is possible that default WP behaviour is broken. Instead, if you do it the WP way, you can be sure, that things like shutdown handlers work as intendedrequire 'vendor/symfony/process/InputStream.php';
, but create a class, make use auf autoloading, etc. -> you would use symfony architecture. And here, you should use WP architecturephp.ini
to give the script more memory