私は基本的にCSVファイルを読み込み、関連するテーブルにデータをインポートするプラグインを作りました。
しかし、このアクションはエラーを引き起こすようです。
致命的なエラー:134217728バイトの許容メモリサイズを使い果たしました(65015808バイトを割り当てようとしました) /var/www/proj/wp-includes/functions.php オンラインで
これは私をfunctions.phpのこのコードに導いた:
function wp_ob_end_flush_all() {
$levels = ob_get_level();
for ($i=0; $i<$levels; $i++)
ob_end_flush();
}
私はGoogleをやったところ、2つの一般的な解決策を見つけましたが、どちらもうまくいかないようでした。
解決策1:zlibを無効にする - これはすでに無効になっています。
解決策2:remove_action('shutdown', 'wp_ob_end_flush_all', 1);
解決策2はまだエラーが発生しますが、メッセージは表示されません。これは厳密には理想的ではありません。
これがエラーの原因となっているスクリプトです。
<?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.');
}
ボタンを押すことでトリガーされ、AJAXを使って処理します。
なぜメモリリークが発生しているのか、あるいはWordPressがもっと有用なエラーメッセージを提供していないのかわかりません。私はその関数を呼んでいません。だから、物事が実行されたときにWordPressがバックグラウンドでやっていることだと思います。
私の情報:
PHP 7.2.10
アパッチ2.4
Linuxミント19
私のサーバーでも起こります:
PHP 7.1.25
アパッチ2.4
CentOS 7.6.1810
WordPressランニングバージョン:4.9.8
正確に何を達成しようとしているかによりますが、WP-CLIコマンドの方が良いかもしれないという Tomのコメント に同意します。利点は、コマンドがphpから直接サーバー上で実行され(通常、最大実行時間がない、異なるphp.iniを読み込むなど)、Webサーバーを使用する必要がないことです。
それが不可能な場合、次の最も良い方法はおそらく カスタムRESTエンドポイントを作成することです 。 WordPressには WP_REST_Controller
というクラスがあり、通常私はこれをextend
するクラスを書いてそこから動作します。簡単にするために、次の例では継承を使用していませんが、私は同じ用語を使用します。
1.新しい経路を登録する
新規/カスタムルートは register_rest_route()
で登録されています
$version = 1;
$namespace = sprintf('acme/v%u', $version);
$base = '/import';
\register_rest_route(
$namespace,
$base,
[
[
'methods' => \WP_REST_Server::CREATABLE,
// equals ['POST','PUT','PATCH']
'callback' => [$this, 'import_csv'],
'permission_callback' => [$this, 'get_import_permissions_check'],
'args' => [],
// used for OPTIONS calls, left out for simplicity's sake
],
]
);
これにより、経由できる新しいルートが作成されます。
http://www.example.com/wp-json/acme/v1/import/
default REST start-^ ^ ^
namespace with version-| |-base
2.パーミッションチェックを定義します
たぶんあなたは認証が必要ですか?ナンスを使用しますか?
public function get_import_permissions_check($request)
{
//TODO: implement
return true;
}
3.実際のエンドポイントコールバックを作成します
以前に定義されたメソッドは WP_REST_Request
オブジェクトを渡され、リクエストボディなどにアクセスするためにそれを使用します。一貫性を保つために、通常JSONのカスタム印刷の代わりに WP_REST_Response
を返すのが最善です。
public function import_csv($request)
{
$data = [];
// do stuff
return new \WP_REST_Response($data, 200);
}
これらすべてをOOPスタイルで行うと、次のクラスが得られます。
class Import_CSV
{
/**
* register routes for this controller
*/
public function register_routes()
{
$version = 1;
$namespace = sprintf('acme/v%u', $version);
$base = '/import';
\register_rest_route(
$namespace,
$base,
[
[
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [$this, 'import_csv'],
'permission_callback' => [$this, 'get_import_permissions_check'],
'args' => [],
],
]
);
}
/**
* endpoint for POST|PUT|PATCH /acme/v1/import
*/
public function import_csv($request)
{
$data = [];
// do stuff
return new \WP_REST_Response($data, 200);
}
/**
* check if user is permitted to access the import route
*/
public function get_import_permissions_check($request)
{
//TODO: implement
return true;
}
}
しかし、まだ404?はい、単純にクラスを定義するだけではうまくいきません(デフォルトではオートロードされていません:()。そのため(プラグインファイルで)register_routes()
を実行する必要があります。
require_once 'Import_CSV.php';
add_action('rest_api_init', function(){
$import_csv = new \Import_CSV;
$import_csv->register_routes();
});
あなたの関数ファイルで、このようなことをしてください:
add_action('wp_ajax_import_parts_abc', 'import_parts_abc');
function import_parts_abc() {
include_once('/admin/inc/scripts/import-parts.php');
exit();
}
次に、あなたのjsファイルに次のようなものを書いてください。
jQuery(document).ready(function() {
jQuery('.my-button').click(function() {
jQuery.ajax({
type: 'GET',
url: ajaxurl,
data: {
action : 'import_parts_abc'
},
success: function(textStatus){
alert('processing complete')
},
error: function(MLHttpRequest, textStatus, errorThrown){
console.log(errorThrown);
}
});
});
});