1

I have a tool that checks available names in a game using cURL, and it should show available ones to the user running the check, and just dismiss the taken ones. Basically when you run a test on a list of names, it checks them all and inserts them into the database with their availability, either 0 for taken or 1 for available.

Right now, the page doesn't load until the check is done, then they're all inserted as a whole in the database. I'd like it to dynamically show the status of the check (i.e. "Checked name 405/4938") and show a loading image while the names are being checked, while also displaying available names as they're checked. Then at the end, remove the loading image, and just have the now complete list of available names.

How should I go about doing this? I don't know much about jQuery/AJAX. It's not ideal right now that they're all displayed as a mass at the end.

This is the code on the name check thread.

<?php
$query = mysql_query("SELECT * FROM `lists` WHERE id = '$id'");
while($row = mysql_fetch_assoc($query)) {

    $id = $row['id'];
    $list = $row['list'];

    foreach(explode("\n", $list) as $listItem) {
        checkname_RS(trim($listItem));
    }
}

This is the function for the check.

<?php   
function checkname_RS($name) {

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "http://services.runescape.com/m=hiscore/compare.ws?user1=" . $name);
    curl_setopt($ch, CURLOPT_HTTPGET, TRUE); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    $output = curl_exec($ch);


    if (stristr($output,"Skill Stats")) {
        mysql_query("INSERT INTO names (name, plat, status) VALUES ('$name', 'Runescape', '0')") or die(mysql_error());
    }
    if (stristr($output,"Member Rankings")) {
        mysql_query("INSERT INTO names (name, plat, status) VALUES ('$name', 'Runescape', '1')") or die(mysql_error());
    }

    curl_close($ch);

}
RandomSeed
  • 29,301
  • 6
  • 52
  • 87
  • 1
    Thanks @brasofilo for the suggestion. I will take it in account. – Rubens Mariuzzo Jul 20 '13 at 01:58
  • @brasofilo and RubensMariuzzo - I appreciate the edits, I wasn't sure exactly how to word everything since it's a bit new to me. I didn't mean to ask for "the best way", but instead for "**A** way". –  Jul 20 '13 at 01:59
  • AFAIK, Ajax is the way to do this. Never done something like this, but I suppose doing a jQuery loop and dispatching ranges of `inserts` will do. Check this search results: http://stackoverflow.com/search?q=%5Bajax%5D+%5Bmysql%5D+increase+results – brasofilo Jul 20 '13 at 02:07
  • Looked through that search, and also on Google, but to no avail. Maybe I'm not searching for the right things, but I can't find an answer. –  Jul 20 '13 at 02:12
  • 1
    This is a progress indicator, so search for jQuery progress bar – Barmar Jul 20 '13 at 02:59
  • @Barmar - That's not all it is, please re-read the question. –  Jul 20 '13 at 12:17
  • How is your `lists` table populated? Is the list submitted by the user when she wants to run a check (and then the check is started immediately)? – RandomSeed Jul 23 '13 at 21:44
  • The list is submitted by the user, then it shows the list, and they can click "run" on it, leading to the "run function". –  Jul 23 '13 at 21:46
  • I suppose `$id` is a user ID and one user may have several lists, is this correct? – RandomSeed Jul 23 '13 at 22:34
  • $id is the list ID. All users can use any list, for now. I need to change it soon, but that's not what we're focused on here. –  Jul 24 '13 at 00:36
  • How about checking portion of a list at a time so you would be able to update each portion after checked in database, lets say you have 20 usernames, check 5 at a time and update those 5 with icon(empty or used) – insanebits Jul 26 '13 at 08:47

3 Answers3

0

You don't really need to do anything special in order to make this happen. Since you're already just keeping the connection open and dumping data at the bottom of the document, you should just be able to dump more javascript to the client after each item or each n items.

So you could dump some html like the following at some point in the document:

<div id="progressMessage"></div> 
<ul id="availableNames"></ul>

Then your thread would just need to output js to update the view as it goes along, for instance (jQuery used for simplicity):

$("#progressMessage").text("1 of 5999");
$("#availableNames").append("<li>matchedName</li>");

The same goes for loading/hiding the progress bar, the js doesn't get run until the client receives it so the net effect is like a progress bar.

See also: https://stackoverflow.com/a/2920207/486620

Community
  • 1
  • 1
Kevin Stricker
  • 17,178
  • 5
  • 45
  • 71
0

A way of doing this is with a Job Queue Server, with workers (tasks that gets the job done) and clients, that ask this workers about the completion of the process. One of the many is Gearman.

Another way of doing it is to run a task yourself using cURL, but do not wait for completion and with set_time_limit(0), and querying the status yourself, for example, by saving the status of the job in a mysql table.

Here some examples on multithreading:
https://github.com/petewarden/ParallelCurl
http://semlabs.co.uk/journal/object-oriented-curl-class-with-multi-threading

The implicit flush not always works.

Hope, it works for you!

Matias P.
  • 64
  • 4
0

A naive solution could be maintaining a numeric processed column in your list table, that you would update at each iteration:

<?php
    // user clicks on "run" (Ajax POST'ing of the input form)

    // load whatever you need from $_SESSION here and then
    session_write_close();
    // so as to allow access to the session to the later Ajax polling

    // reset counter
    mysql_query("UPDATE lists SET processed = 0 WHERE id = $id");
    // then the rest of your current script


    function checkname_RS($name) {
        // your current code here
        // then increment the counter
        mysql_query("UPDATE lists SET processed = processed + 1 WHERE id = $id");
    }

Then have an Ajax call poll the database every now and then, in order to check the status of your current job. The Ajax call would read the status from a script like this:

<?php
    // script handling the Ajax call

    // get the number of names already checked
    mysql_query("SELECT processed FROM lists WHERE id = $id");

    // proposed trick to count the number of names in this list
    mysql_query("SELECT CHAR_LENGTH(names) - CHAR_LENGTH(REPLACE(names, '\n', '')) +1 AS cnt FROM lists WHERE id = $id");
    // better computing this number once and for all at the beginning
    // of the process and (eg.) store it in session

    // generate the response to the Ajax call here

The result can be displayed in a div, or be used to update a JQuery UI Progressbar, or in any way you find suitable.


Just one piece of advice regarding your structure. Always avoid storing non-scalar data in a column (such as your "\n"-separated list of names). Instead store one row per value (one row per name). Everything becomes so much simpler (and usually more efficient) when you normalise your data, even for small projects.

RandomSeed
  • 29,301
  • 6
  • 52
  • 87
  • I can't make users copy/paste names 1 by 1 into new inputs... They copy/paste lists of thousands of names at once. –  Jul 25 '13 at 21:56
  • @Revolt Of course not. But you can parse their input automatically before insetion. – RandomSeed Jul 25 '13 at 21:58
  • Hmm that could work. As for your actual answer, I'll give it a shot in a bit when I get home and have my files. Thanks for it, I'll check if it works shortly. –  Jul 25 '13 at 22:06
  • If you decide to go with the "one row per name" option, check the first version of my answer, when I was assuming this kind of structure. – RandomSeed Jul 25 '13 at 22:13
  • I can see a couple of issues with my suggested approach. The first and obvious one is that the click on "Run" should trigger an Ajax `POST` and not a regular form submission. Or else, the page will not load until after the job is completed. The second issue has to do with concurrent access to the same session (I assume you will use sessions). Only one PHP thread may access a given session at a time. The session should be closed ASAP. Please see my updated answer. – RandomSeed Jul 26 '13 at 23:24
  • Although not the answer I was hoping for, the bounty goes to you since it expires soon. Mostly for your last tid-bit of advice. But your first thing didn't help much. –  Jul 28 '13 at 21:54