Schedule php wordpress script to run in background - php

I hope I am not expecting too much. Any help would be extremely useful for me, because I am stuck for days now.
I created a relatively simple wordpress plugin in php.
What is the plugin supposed to do ?
-The plugin is supposed to communicate with external api and import product data in json file.
Import is started by pressing "start import" button that is created by pluin in the wordpress menu - import products.
Example request:
curl -X GET
-H 'X-Api-Key: [api-key]'
https://example.com/products/[PRODUCT ID]
[PRODUCT ID] is supposed to range from 1 to 10000
Plugin receives json file with product feed for every single request - every single [PRODUCT ID]
Plugin creates a woocommerce product and attaches imported information.
Does the plugin work ?
Yes and no, It imports first 100 products (in about 1min) correctly and then it just stops, sometimes resulting in error related to the request taking too much time to finish.
I know that the plugin doesn't work because the import script is executed in the browser and gets timed out.
I also know that I should do this process in the background, split it into batches and queue execution. The thing is I tried many ways to do so but failed miserably.
I have composer and action scheduler installed.
Unfortunately everytime I try to split this into batches, use action scheduler It just doesn't work or imports first product and stops.
I know that I'm dealing with large amount of products, I don't have error handling, checking if imported product exists etc, but I really have to run this import once
so there is no need to make this plugin very refined. This has to run, import products and I can get rid of it.
I do have wp debug on, so my attepmts on using action scheduler didn't create any errors or fatal errors, but it just didn't work properly.
I have 1500MB Ram available, this is shared hosting server, I/O 1MB, 60 available processes, 88000/600000 Inodes used, 5/200GB disc space used.
I increased php maxExecutionTime to 3000, memorylimit 1536M, maxInputTime 3000, but that didn't change anything.
I'm attaching my working code below. This is the version without my poor attemts on using action scheduler, splitting it into batches and running it in the backgroud.
I feel like this is going to be easier to read.
This code below runs in the web browser and works, but gets timed out.
I will be extremely grateful for any help with running it in the background.
Is it possible to just run this script from SSH linux terminal so it doesn't get timed out ?
`
<?php
/**
* Plugin Name: Product Importer
* Description: Imports products from an external API
* Version: 1.0
* Author: me
* Author URI: http://www.example.com
*/
// Include the Autoscheduler library
require_once '/home/user/vendor/woocommerce/action-scheduler/action-scheduler.php';
add_action('admin_menu', 'add_import_button');
function add_import_button() {
add_menu_page('Import Products', 'Import Products', 'manage_options', 'import_products', 'import_products_page');
}
function import_products_page() {
echo '<h1>Import Products</h1>';
echo '<form method="post">';
echo '<input type="submit" name="start_import" value="Start Import">';
echo '</form>';
if (isset($_POST['start_import'])) {
import_function();
}
}
function import_function() {
$product_id = 1;
while($product_id < 10000){
$product_id++;
$api_key = 'my-api-key';
$headers = array(
'X-Api-Key: ' . $api_key,
);
$url = 'https://example.com/products/';
$product_url = $url . $product_id;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $product_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$product_data = json_decode($response, true);
// post array etc
// Set other product data as required.
}
}
}
`

One of the ways to solve this issue is using recursion where the code can run in the background. Take a look at the example below
require_once '/home/user/vendor/woocommerce/action-scheduler/action-scheduler.php';
add_action('admin_menu', 'add_import_button');
function add_import_button() {
add_menu_page('Import Products', 'Import Products', 'manage_options', 'import_products', 'import_products_page');
}
function import_products_page() {
echo '<h1>Import Products</h1>';
echo '<form method="post">';
echo '<input type="hidden" name="product_id" value="1">'; // optional
echo '<input type="submit" name="start_import" value="Start Import">';
echo '</form>';
if (isset($_POST['start_import'])) {
import_function();
}
}
// AJAX function
add_action( 'wp_ajax_nopriv_import_function', 'import_function' );
add_action( 'wp_ajax_import_function', 'import_function' );
function import_function() {
$product_id = ( ! empty( $_POST['product_id'] ) ) ? $_POST['product_id'] : 1;
$url = 'https://example.com/products/';
$args = array(
'headers' => array(
'Content-Type' => 'application/json',
'X-Api-Key' => 'apikey12345'
)
);
// this call the function and return the body
$results = wp_remote_retrieve_body(wp_remote_get($url . $product_id, $args));
// convert to array
$results = json_decode( $results );
// Stop the code execution on this conditions
if( ! is_array( $results ) || empty( $results ) ){
return false;
}
// Do your product creation here...
$product_id++; // increase $product_id
wp_remote_post( admin_url('admin-ajax.php?action=import_function'), [
'blocking' => false, // needed for the script to continue running on the background
'sslverify' => false, // needed if working on localhost.
'body' => [
'product_id' => $product_id
]
] );
}

OK, I managed to get it to work. I assume that it's dumb way to do this, but since it works it's fine for me. The only thing that is problematic now is the fact that the code for image import that I used previously (when code ran in browser) now makes the plugin stuck. Without it it works great and fast. Here is my current code:
<?php
/**
* Plugin Name: Product Importer
* Description: Imports products from an external API
* Version: 3.0
* Author: me
* Author URI: http://www.example.com
*/
register_activation_hook( __FILE__, 'schedule_import' ); //plugin starts when activated, fine for me
function schedule_import() {
wp_schedule_single_event( time(), 'import_products' );
}
add_action( 'import_products', 'import_function' );
function import_function() {
$batch_size = 50; //when split into batches it worked faster than one products after another, so ok
$batch_delay = 60; //give it some time to finish batch, to avoid problems
$last_imported_product_id = get_option( 'last_imported_product_id', 1 ); //need it for incrementation and rescheduling
$api_key = 'apikey123456789';
$headers = array(
'X-Api-Key: ' . $api_key,
);
$url = 'https://example.com/products/';
for ( $i = 0; $i < $batch_size; $i++ ) {
$product_id = $last_imported_product_id + $i;
$product_url = $url . $product_id;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $product_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$product_data = json_decode($response, true);
$post = array(
'post_title' => $product_data['name'],
'post_content' => $product_data['description'],
'post_status' => 'publish',
'post_type' => 'product',
'meta_input' => array(
'_virtual' => 'yes',
'_regular_price' => $product_data['price'],
),
);
$post_id = wp_insert_post($post);
update_post_meta($post_id, '_sku', $product_data['Id']);
wp_set_object_terms($post_id, 'external', 'product_type');
$external_url = 'https://example.external.url';
update_post_meta( $post_id, '_product_url', $external_url );
update_option( 'last_imported_product_id', $product_id + 1 ); //incrementation of product_id for rescheduling
wp_schedule_single_event( time() + $batch_delay, 'import_products' ); //rescheduling
}
}
This above works well. However when I add my old code for image imports it imports nothing or 1 product (without image lol) and becomes stuck. After a minute it imports the same product over and over again. Old code for image import:
// Get the product images
$images = $product_data['images']['screenshots'];
$image_ids = [];
foreach ($images as $image) {
// Download the image
$image_url = $image['url'];
$response = wp_remote_get($image_url);
if (is_array($response)) {
$image_data = $response['body'];
$filename = basename($image_url);
$file_array = array(
'name' => $filename,
'tmp_name' => download_url($image_url),
);
// Insert the image into the media library
$attach_id = media_handle_sideload($file_array, $post_id);
if (!is_wp_error($attach_id)) {
array_push($image_ids, $attach_id);
}
}
// Set the product image gallery
update_post_meta($post_id, '_product_image_gallery', implode(',', $image_ids));
// Set the product featured image
update_post_meta($post_id, '_thumbnail_id', $image_ids[0]);
And no, I don't put it after rescheduling, but after $post array.
Honestly no idea why it would crash, tried smaller batches, longer wait between batches etc. I assume the code for images import is just very poorly written. Any solutions for this problem are going to be very appreciated ! :)
PS: I am totally fine with importing the first image available (there is around 6) and setting it up as thumbnail for product. I can give up on the gallery since It's going to slow down the process anyway.
EDIT: here is the fragment of json file received from api. (for better context of importing images):
"images":{
"screenshots":[
{
"url":"https://example.jpg",
"thumbnail":"https://example.jpg"
},
{
"url":"https://example.jpg",
"thumbnail":"https://example.jpg"
},
{
"url":"https://example.jpg",
"thumbnail":"https://example.jpg"
etc....

Related

How to display all data from Invision Power board Rest API

I am looking to display all data but my result is only the first 25 for page 1:
I want to extract all the download file name for example
what would be the best way to get all the data
documentation :https://invisioncommunity.com/developers/rest-api
<?php
$communityUrl = 'https://www.example.com/ips4/';
$apiKey = 'c7a349a1629f02cd2855a58d77646f6d';
$endpoint = '/downloads/files';
$endpoint = '/core/members';
$curl = curl_init( $communityUrl . 'api' . $endpoint );
curl_setopt_array( $curl, array(
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => "{$apiKey}:"
) );
$response = curl_exec( $curl );
?>
My php code
<?php
$result_files = json_decode($response , true);
if (is_array($result_files)) {
foreach ($result_files as $value) {
if (is_array($value)) {
foreach ($value as $info) {
echo $info['title'];
}
}
}
?>
The code above display only 25 files. I have 150.
How to do that ?
Thank you
Have you looked the rest download document, there are page that defaults to 25 files, you have to send call to fetch 150 files per page. Use the perPages atripute to declare amount per page.
page int Page number
perPage int Number of results per page - defaults to 25
After looking rest commands here id put perPage = 150 to collect 150 files in 1 call. Try this:
$endpoint = '/downloads/files/{perPage=150}';
or
$endpoint = '/downloads/files?perPage=150'
One or another way will work to get 150 results.
Here is dynamic version if you will later first get dynamically how many files there is and then fetch that result files amount:
$endpoint = '/downloads/files?perPage='.$amount

Wordpress shortcode fires api call when editing post

I created a wordpress plugin with a add_shortcode filter that sends an api call to our system and retrieves documents from the server. However, I noticed that it fires even in the Admin area while I am editing a post or a page. So as I type the value inside the shortcode, it continuously communicates with the server.
How do I stop the shortcode from firing the api call function unless it was published in the post/page?
Edit- Adding more info
The comments below are putting me on the right track, so I want to add more info for you guys.
I am using the WordPress Boilerplate. I added the add_shortcode filter to the plugin library and I register the add_shortcode in the define_public_hook:
$this->loader->add_shortcode( 'my_plugin', $plugin_public, 'my_shortcode', 10, 2 );
then in my-plugin-public-display.php I add the functions (I'll abbreviate it...):
public function api_get_as_json( $controller, $slug, $action, $params, $endpoint ) {
if ( null == $params ) {
$params = array();
}
// Create URL with params
$url = $endpoint . $controller . $slug . $action . '?' . http_build_query($params);
// echo $url;
// Use curl to make the query
$ch = curl_init();
curl_setopt_array(
$ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
)
);
$output = curl_exec($ch);
// Decode output into an array
$json_data = json_decode( $output, true );
curl_close( $ch );
return $json_data;
}
/**
* Shortcodes
*
* #since 1.0.0
*/
public function my_shortcode( $atts ) {
//turn on output buffering to capture script output
ob_start();
// Get attributes from shortcode
$attributes = shortcode_atts( array(
...
), $atts, 'my_plugin' );
$my_array = //getting the array attributes here...
/* Send API calls to WhoTeaches, get profile and Packages */
$result = $this->api_get_as_json('controller/', null, 'action', $my_array, $host . "domain.com/api/v1/");
// Load the view
include (plugin_dir_path( dirname( __FILE__ ) ) . 'public/partials/my-plugin-public-display.php');
// Get content from buffer and return it
$output = ob_get_clean();
return $output;
// Clear the buffer
ob_end_flush();
}
Do I need to add or edit something here?

Function in PHP file doesn't work, when file is called using curl

I've got on my server PHP file, which download something using curl from another server, and save it to db in nested php function. This process is little time-consuming, when I open it in my browser, I must wait ca. 1 minute, but all downloaded records are correct.
Problem is in CRON wget/curl download. When I use
wget http://myserver/myscript.php, or curl http://myserver/myscript.php, connection is closed after 1 byte, and nothing happens on server...
Where make I mistake? Maybe some headers? Why wget/curl don't wait on end of my PHP function like browser? I hope, that require of wp-load.php (I must use for it some Wordpress functions) isn't problem?
Many thanks for responses
Code:
<?php
define('WP_USE_THEMES', false);
require_once("wp-load.php");
$licznik = 0;
// WP_Query arguments
$args = array (
'post_type' => array( 'easy-rooms' ),
'posts_per_page' => 30
);
// The Query
$query = new WP_Query( $args );
// The Loop
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$fields = array(
"funkcja" => "lista_rezerwacji",
"id_pokoju" => get_the_ID()
);
$postvars = '';
foreach($fields as $key=>$value) {
$postvars .= $key . "=" . $value . "&";
}
rtrim($fields_string, '&');
$url = "http://some.remote.script.to.download.sth.by.curl";
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POST, 1); //0 for a get request
curl_setopt($ch,CURLOPT_POSTFIELDS, $postvars);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT ,3);
curl_setopt($ch,CURLOPT_TIMEOUT, 120);
$response = curl_exec($ch);
curl_close ($ch);
echo $response;
//THIS IS FUNCTION IN SOME WORDPRESS PLUGIN, WHICH DOESN'T WORK WHEN I WGET/CURL THIS SCRIPT
set_reservations($response, get_the_ID());
$licznik++;
}
} else {
// no posts found
}
// Restore original Post Data
print_r($licznik);
wp_reset_postdata();
?>

Update single product stock in woocommerce using API from import.io

I have a problem.
I'm trying to use an API from import.io for manage automatically the stock of products I reselling.
The API returns a value to me and I use this value to determine if the product is in stock or not.
Each group/category uses a different API (because each group is a different origin shop. This code is only for one category, I need to figure out how to make that works for dev the others).
I created this code and I'm trying to make it work in function.php
I have a custom field that has the url source to each product and I want to use it as a parameter to the api.
After checking which group is the product, it gets the data so I can update the stock.
The problem is that I do not know how to update the stock of WooCommerce in this way :O And I'd like to have that verification was done when the single product page was opened by the customer.
Can someone take a look at my code and give me some suggestions?
Thank you!
$userGuid = "c5ed744c-7c10-46d1-9c43-22c6eef5aaca";
$apiKey = "private";
// Issues a query request to import.io
function query($connectorGuid, $input, $userGuid, $apiKey) {
$url = "https://query.import.io/store/connector/" . $connectorGuid . "/_query?_user=" . urlencode($userGuid) . "&_apikey=" . urlencode($apiKey);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
"import-io-client: import.io PHP client",
"import-io-client-version: 2.0.0"
));
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array("input" => $input)));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$result = curl_exec($ch);
curl_close($ch);
return json_decode($result);
}
//This is a custom field
$url_font = apply_filters('custom_url_font', get_post_meta($post->ID, '_url_font', true));
add_action('woocommerce_before_shop_loop_item_title','category_stock');
function category_stock() {
global $woocommerce,$product, $post;
$post_id = $post->ID;
$groupcat = 608; // category id for the this api
$terms = get_the_terms( $post_id, 'product_cat' ); //get taxonamy of the products
if ( $terms && ! is_wp_error( $terms ) ) :
foreach ( $terms as $term ) {
$catid = $term->term_id;
if($groupcat == $catid) {
$result = query("d9df1137-b402-40ef-b472-35db84684fbe", array(
"webpage/url" => $url_font,//this is the url from the custom field
), $userGuid, $apiKey, false);
if(($result->results[0]->stock)=="INSTOCK")
{
update_post_meta($post->ID, '_stock', '10');
}
else{
update_post_meta($post->ID, '_stock', '0');
}
}
}endif;
}

Post to wordpress via php

I'm trying to post on my wordpress site using php .
I used php to fetch data from a website and stored all of them in variables .
I found the code to a few auto wordpress php posters but they are kind of complex and i'm not really sure how to use/alter them.
What the simplest way to do it via php ?
My data are like :
$topic_name = "name";
$mainimage = "url/image":
$input = "hello................." ;
$category = "cars";
$tags = ("tag1","tag2","tag3"...);
Note : I just need the basic code to login to my wordpress and post some random text via php - I'm pretty sure i can figure out how to input the category , tags etc later on.
I'm trying to use this one as it seem simple but i don;t think it works for the latest version of wordpress (3.7.1 ) -
I am using xampp for hosting the site locally for now
If anyone can modify it or can share a working one , would be great .
function wpPostXMLRPC($title,$body,$rpcurl,$username,$password,$category,$keywords='',$encoding='UTF-8') {
$title = htmlentities($title,ENT_NOQUOTES,$encoding);
$keywords = htmlentities($keywords,ENT_NOQUOTES,$encoding);
$content = array(
'title'=>$title,
'description'=>$body,
'mt_allow_comments'=>0, // 1 to allow comments
'mt_allow_pings'=>0, // 1 to allow trackbacks
'post_type'=>'post',
'mt_keywords'=>$keywords,
'categories'=>array($category)
);
$params = array(0,$username,$password,$content,true);
$request = xmlrpc_encode_request('metaWeblog.newPost',$params);
$ch = curl_init();
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
curl_setopt($ch, CURLOPT_URL, $rpcurl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
$results = curl_exec($ch);
curl_close($ch);
return $results;
}
Do you really need to use XML-RPC? This is generally what you would want to do to post to a remote WordPress installation. For instance, from a completely different site, from a mobile app, etc.
It sounds like you are writing a plugin that will run within your WordPress installation. In that case you can just call wp_insert_post() directly.
A very trivial example from WordPress's wiki, which also has a complete list of parameters you can use:
// Create post object
$my_post = array(
'post_title' => 'My post',
'post_content' => 'This is my post.',
'post_status' => 'publish',
'post_author' => 1,
'post_category' => array(8,39)
);
// Insert the post into the database
wp_insert_post( $my_post );

Categories