/** * Handles downloaded data. * The function name is a bit of a misnomer, as it does not actually fetch the contents when using api_multi. This has been done ahead of time (unless we're using the old api() calls). * This function writes the downloaded content (json-ified, if it's not a Picture) into its respective output file, causes the new object to create its connections and add them to the queue. * @param $facebook A Facebook instance to use for fetching data. */ public function fetch($facebook) { // If the folder for our output files doesn't exist yet, create it if (!is_dir("../tmp/" . $facebook->getUnique())) { mkdir("../tmp/" . $facebook->getUnique()); } // Create a safe file name. Simply replaces all slashes, actually. //TODO: Write this platform-independent-safe $fname = Connection::createSafeName($facebook, $this->url); //$fname = $facebook->getUnique() . "/" .strtr($this->url, "/", "~") . ".request"; // Is this a Picture? If so, we don't process the content but simply write it into a file that has base64($url) as its filename. if ($this->type == 'Picture') { fprintf($facebook->getLogFd(), "Writing picture with filesize " . strlen($this->json) . "\n"); if (!file_exists("../tmp/" . $facebook->getUnique() . "/" . base64_encode($this->url))) { file_put_contents("../tmp/" . $facebook->getUnique() . "/" . base64_encode($this->url), $this->json); } return new Picture("", 0); } try { // Check if the file already exists; if so, throw an exception if (file_exists($fname)) { throw new Exception("File " . $fname . " already exists."); } // If json is empty, we haven't fetched any content yet, which means that we're using the old API. // So let's just use the old api() call. This one also does an implicit json_decode(), so we don't have to perform that anymore. if (strlen($this->json) < 1) { $this->json = $facebook->api($this->url); } else { $facebook->log("[RESPONSE] Response's json is larger than 0"); $this->json = json_decode($this->json, true); } // Check if the Graph API returned an error. if (isset($this->json['error'])) { //echo "fetch() FB Error:<br />"; //print_r($this->json); throw new Exception("fb error: " . json_encode($this->json['error'])); } } catch (Exception $e) { //echo "fetch() Exception occurred (" . $e->getMessage() . "), continuing anyway, handling as empty Picture"; //ob_flush(); //flush() $facebook->log("[ERROR] fetch() Exception occurred (" . $e->getMessage() . "), continuing anyway, handling as empty Picture"); // This "empty picture" is nearly an empty object. It has no connections and should therefore be completely neutral to the rest of the process. return new Picture("", 0); } // Open the output file for writing $facebook->log("[FILE] Creating file " . $fname); $fp = fopen($fname, "w"); // Write the json data - in text form - to the file //fwrite($fp, print_r($this->json, TRUE)); fwrite($fp, json_encode($this->json)); // Close the output file again. fclose($fp); if (isset($this->json['paging']) && isset($this->json['paging']['next'])) { $queue = new PriorityQueue(); $url = substr($this->json['paging']['next'], strpos($this->json['paging']['next'], "com/") + 4); $facebook->log("[DEBUG] Adding paging URL " . $url); $queue->unshift(new Connection($url, $this->depth, $this->type, true), 100); Facebook::getQueue()->merge($queue); } else { $facebook->log("[DEBUG] No paging or next"); } // If the data is not "right there" at the topmost level but nested in the data part, replace our internal variable with the actual payload. if (isset($this->json['data'])) { $this->json = $this->json['data']; } // Check if there are multiple objects stored in the received json if ($this->multiplicity) { $retval = array(); // Handle each object in json foreach ($this->json as $item) { // First, the two "meta-types" Profile and Taggable; they're not actual types, but they can determine which of their subtypes is the appropriate one with their static getInstance() method. if ($this->type == 'Profile') { array_push($retval, Profile::getInstance($item, $this->depth)); } else { if ($this->type == 'Taggable') { array_push($retval, Taggable::getInstance($item, $this->depth)); } else { // Slight PHP magic: $this->type is a string that contains a class name, i.e. we construct an object whose class name is specified by that field. array_push($retval, new $this->type($item, $this->depth)); } } if ($this->type == 'User') { $facebook->log('Created a user.'); } //$facebook->log(print_r($item)); } $fullnull = true; //Performing getConnections() now, adding everything into the big static queue // Also, we check if all getConnections() return NULL foreach ($retval as $item) { if (NULL != $item->getConnections()) { $fullnull = false; } } // All getConnections() have returned NULL, which means that the depth is too high (or deep?). So it's time for us to return NULL, too, in order to let the recursion end at this point (actually, it doesn't end, it just switches to a different part of the recursion tree). if ($fullnull) { return NULL; } // Return the array with all parsed objects return $retval; } else { // Same as before: Call getInstance for the meta-types, otherwise create an object with the type $this->type. if ($this->type == 'Profile') { $retval = Profile::getInstance($this->json, $this->depth); } else { if ($this->type == 'Taggable') { $retval = Taggable::getInstance($this->json, $this->depth); } else { $retval = new $this->type($this->json, $this->depth); } } //Performing getConnections() now, adding this element's connection into the big static queue if (NULL == $retval->getConnections()) { return NULL; } // Return the parsed object return $retval; } }