//send the job to the Bulk API and pass back returned JobInfo to the same variable
$job = $myBulkApiConnection->createJob($job);
// STEP 4. CREATE A NEW BATCH
//prep the query and create a batch from it
$soql = "SELECT Id, Name, Email FROM Contact";
$batch = $myBulkApiConnection->createBatch($job, $soql);
//add more and more batches.... (here, we will only do one)
// STEP 5. CLOSE THE JOB
$myBulkApiConnection->updateJobState($job->getId(), "Closed");
// STEP 6: MONITOR BATCH STATUS UNTIL DONE
while ($batch->getState() == "Queued" || $batch->getState() == "InProgress") {
    sleep(10);
    //wait for 5 seconds before polling again. in the real world, probably make this exponential as to not ping the server so much
    $batch = $myBulkApiConnection->getBatchInfo($job->getId(), $batch->getId());
}
// STEP 7: GET BATCH RESULTS
// For queries, use get back an array of resultIds
$resultList = $myBulkApiConnection->getBatchResultList($job->getId(), $batch->getId());
// then retrieve the actual results and place in an array.
// the CSV data is left in raw form for simplicity
foreach ($resultList as $resultId) {
    $queryResults["{$resultId}"] = $myBulkApiConnection->getBatchResult($job->getId(), $batch->getId(), $resultId);
}
// PRINT EVERYTHING THAT HAPPENED ABOVE
print "<pre>" . "PHP BULK API CLIENT SAMPLE CODE OUTPUT\n" . "This is the output of the PHP Bulk API Client Sample Code. View the source code for step-by-step explanations.\n\n";
print "== SOQL QUERY STRING == \n" . htmlspecialchars($soql) . "\n\n";
//print "== QUERY RESULTS == \n" . htmlspecialchars(print_r($queryResults, true)) . "\n\n";
// print "== CLIENT LOGS == \n" . $myBulkApiConnection->getLogs() . "\n\n";
$myBulkApiConnection->clearLogs();
//clear log buffer
print "</pre>";
 public function batchQuery($object, $fields, $startDate = null, $fh = null)
 {
     $this->initSession();
     $myBulkApiConnection = new BulkApiClient($this->session->serverUrl, $this->session->sessionId);
     $myBulkApiConnection->setLoggingEnabled(false);
     $myBulkApiConnection->setCompressionEnabled(true);
     // create in-memory representation of the job
     $job = new JobInfo();
     $job->setObject($object);
     $job->setOpertion('query');
     $job->setContentType('CSV');
     $job->setConcurrencyMode('Parallel');
     $soql = "SELECT " . implode(',', $fields) . " FROM {$object}";
     if ($startDate != null) {
         $soql .= " WHERE LastModifiedDate >= {$startDate}";
     }
     echo 'Creating job...';
     $job = $myBulkApiConnection->createJob($job);
     echo 'ok' . PHP_EOL;
     echo 'Creating batch...';
     $batch = $myBulkApiConnection->createBatch($job, $soql);
     echo 'ok' . PHP_EOL;
     echo 'Closing job...';
     $myBulkApiConnection->updateJobState($job->getId(), 'Closed');
     echo 'ok' . PHP_EOL;
     $sleepTime = 4;
     echo 'Waiting for job to complete...';
     while ($batch->getState() == 'Queued' || $batch->getState() == 'InProgress') {
         // poll Salesforce for the status of the batch
         sleep($sleepTime *= 1.1);
         echo ".";
         $batch = $myBulkApiConnection->getBatchInfo($job->getId(), $batch->getId());
     }
     echo 'ok' . PHP_EOL;
     // get status of batches
     echo "Retrieving results...";
     $resultList = $myBulkApiConnection->getBatchResultList($job->getId(), $batch->getId());
     // retrieve queried data
     foreach ($resultList as $resultId) {
         $myBulkApiConnection->getBatchResult($job->getId(), $batch->getId(), $resultId, $fh);
     }
     echo 'ok' . PHP_EOL;
     if (isset($fh)) {
         $preview = stream_get_contents($fh, 32, 0);
         rewind($fh);
         if (strcasecmp($preview, 'Records not found for this query') == 0 || trim($preview) == false) {
             // return false if no records returned
             return false;
         } else {
             return true;
         }
     }
 }