Refresh
This commit is contained in:
226
app/Models/Traits/Document/AnalysesDocument.php
Executable file
226
app/Models/Traits/Document/AnalysesDocument.php
Executable file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits\Document;
|
||||
|
||||
use App\Models\Bookmark;
|
||||
use App\Models\Feed;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use SimplePie;
|
||||
use Storage;
|
||||
|
||||
trait AnalysesDocument
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Properties |-------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Provides temporary access to response to analyzers.
|
||||
*
|
||||
* @var \Illuminate\Http\Client\Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Provides temporary access to document's body to analyzers.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $body;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Methods |----------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin document analysis.
|
||||
*/
|
||||
public function analyze()
|
||||
{
|
||||
// Don't bother if document isn't bookmarked anymore
|
||||
if ($this->isOrphan() && $this->wasOrphanFor(config('cyca.maxOrphanAge.document'))) {
|
||||
$this->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fetchContent();
|
||||
|
||||
if (empty($this->response)) {
|
||||
$this->checked_at = now();
|
||||
|
||||
$this->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($homepage = $this->isFeed()) {
|
||||
$this->convertToFeed($homepage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->existingDocumentsMerged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->runAnalyzers();
|
||||
|
||||
$this->checked_at = now();
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy content of resource at document's URL in "local" storage.
|
||||
*/
|
||||
protected function fetchContent()
|
||||
{
|
||||
$storageRoot = $this->getStoragePath();
|
||||
$bodyFilename = $storageRoot.'/body';
|
||||
$responseFilename = $storageRoot.'/response.json';
|
||||
$debugFilename = $storageRoot.'/debug';
|
||||
|
||||
Storage::put($debugFilename, null);
|
||||
|
||||
# $debugStream = fopen(storage_path('app/'.$debugFilename), 'w');
|
||||
#
|
||||
try {
|
||||
$this->response = Http::withOptions(array_merge([
|
||||
# 'debug' => env('APP_DEBUG') ? $debugStream : false,
|
||||
], config('http_client')))->timeout(30)->get($this->url);
|
||||
|
||||
$this->body = $this->response->body();
|
||||
} catch (\Exception $ex) {
|
||||
report($ex);
|
||||
# } finally {
|
||||
# fclose($debugStream);
|
||||
}
|
||||
|
||||
if (!$this->response) {
|
||||
return;
|
||||
}
|
||||
|
||||
$psrResponse = $this->response->toPsrResponse();
|
||||
|
||||
$responseData = [
|
||||
'headers' => $this->response->headers(),
|
||||
'protocol_version' => $psrResponse->getProtocolVersion(),
|
||||
'response' => $this->response,
|
||||
];
|
||||
|
||||
Storage::put($responseFilename, json_encode($responseData));
|
||||
|
||||
if ($this->response->ok()) {
|
||||
Storage::put($bodyFilename, $this->body);
|
||||
|
||||
$this->mimetype = Storage::mimetype($bodyFilename);
|
||||
}
|
||||
|
||||
$this->http_status_code = $this->response->status();
|
||||
$this->http_status_text = $this->response->getReasonPhrase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly determine if document is, in fact, a feed, and return
|
||||
* corresponding home page URL.
|
||||
*
|
||||
* @return bool|string Return feed's home page if really a feed, false otherwise
|
||||
*/
|
||||
protected function isFeed()
|
||||
{
|
||||
$client = new SimplePie();
|
||||
|
||||
$client->enable_cache(false);
|
||||
$client->set_raw_data($this->body);
|
||||
|
||||
if ($client->init()) {
|
||||
return urldecode($client->get_permalink());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform this document into a feed, by creating or using an existing
|
||||
* document with provided homepage URL, creating or using an existing feed
|
||||
* with current document's URL, linking both, updating any references to
|
||||
* this document to point to the new document, and finally deleting this
|
||||
* document.
|
||||
*
|
||||
* @param string $homepage
|
||||
*/
|
||||
protected function convertToFeed($homepage)
|
||||
{
|
||||
$document = self::firstOrCreate(['url' => $homepage]);
|
||||
$feed = Feed::firstOrCreate(['url' => $this->url]);
|
||||
|
||||
if (!$document->feeds()->find($feed->id)) {
|
||||
$document->feeds()->attach($feed);
|
||||
}
|
||||
|
||||
Bookmark::where('document_id', $this->id)->update(['document_id' => $document->id, 'initial_url' => $homepage]);
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find another document having the same real URL. If one is found, we will
|
||||
* update all bookmarks to use the oldest document and delete this one.
|
||||
*
|
||||
* Returns true if documents have been merged.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function existingDocumentsMerged()
|
||||
{
|
||||
$realUrl = urldecode((string) $this->response->effectiveUri());
|
||||
|
||||
if ($realUrl !== $this->url) {
|
||||
$document = self::where('url', $realUrl)->first();
|
||||
|
||||
if ($document) {
|
||||
Bookmark::where('document_id', $this->id)->update(['document_id' => $document->id]);
|
||||
|
||||
$allBookmarks = Bookmark::where('document_id', $this->id)->get()->groupBy('folder_id');
|
||||
|
||||
foreach ($allBookmarks as $folderId => $bookmarks) {
|
||||
if ($bookmarks->count() > 1) {
|
||||
array_shift($bookmarks);
|
||||
|
||||
foreach ($bookmarks as $bookmark) {
|
||||
$bookmark->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->url = $realUrl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select analyzers for this particular document then run them.
|
||||
*/
|
||||
protected function runAnalyzers()
|
||||
{
|
||||
if (array_key_exists($this->mimetype, config('analyzers'))) {
|
||||
$this->launchAnalyzerFor($this->mimetype);
|
||||
} else {
|
||||
// In doubt, launch HtmlAnalyzer
|
||||
$this->launchAnalyzerFor('text/html');
|
||||
}
|
||||
}
|
||||
|
||||
protected function launchAnalyzerFor($mimetype)
|
||||
{
|
||||
$className = config(sprintf('analyzers.%s', $mimetype));
|
||||
$instance = new $className();
|
||||
|
||||
$instance->setDocument($this)->setBody($this->body)->setResponse($this->response)->analyze();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user