Skip to content

Instantly share code, notes, and snippets.

@atsu666
Last active March 6, 2020 13:22
Show Gist options
  • Select an option

  • Save atsu666/8d090ea15b21a2a995b5c7bcc561ccf1 to your computer and use it in GitHub Desktop.

Select an option

Save atsu666/8d090ea15b21a2a995b5c7bcc561ccf1 to your computer and use it in GitHub Desktop.
php/Services/Preview/Engine.php
<?php
namespace Acms\Services\Preview;
use Acms\Services\Preview\Contracts\Base;
use ACMS_Session;
use App;
use DB;
use SQL;
class Engine implements Base
{
/**
* PHPセッションラッパー
*
* @var \ACMS_Session
*/
protected $session;
/**
* @var
*/
protected $get;
/**
* プレビュー共有URLの有効時間
*
* @var int
*/
protected $lifetime;
/**
* 共有URL
*
* @var string
*/
protected $shareUrl;
/**
* 偽装UAをセッションに保存する時のキー名
*
* @var string
*/
protected $previewFakeUaKeyName = 'preview_fake_ua';
/**
* 偽装UAをセッションを確認するためのトークンのキー名
*
* @var string
*/
protected $previewFakeUaTokenKeyName = 'preview_fake_ua_token';
/**
* プレビュー共有するための認証トークンのキー名
*
* @var string
*/
protected $previewShareUrlTokenKeyName = 'preview-token';
/**
* プレビューモードか判定するために使うURLクエリパラメーター名
*
* @var string
*/
protected $previewModeQueryParameter = 'acms-preview-mode';
/**
* Engine constructor.
*
* @param int $lifetime
* @param string $shareUrl
*/
public function __construct($lifetime, $shareUrl)
{
$app = App::getInstance();
$this->get = $app->getGetParameter();
$this->lifetime = $lifetime;
$this->shareUrl = $shareUrl;
}
/**
* プレビューモード中か判定
*
* @return bool
*/
public function isPreviewMode()
{
if ($session = $this->getSession()) {
$fakeUaToken = $session->get($this->previewFakeUaTokenKeyName, false);
if ($fakeUaToken && $fakeUaToken === $this->get->get($this->previewModeQueryParameter)) {
setConfig('x_frame_options', 'off');
return true;
}
}
return false;
}
/**
* 偽装ユーザーエージェントの取得
*
* @return string | bool
*/
public function getFakeUserAgent()
{
if ($this->isPreviewMode()) {
$session = $this->getSession();
return $session->get('preview_fake_ua', false);
}
return false;
}
/**
* プレビュー共有モードになれるか判定
*
* @return bool
*/
public function isValidPreviewSharingUrl()
{
$previewToken = $this->get->get($this->previewShareUrlTokenKeyName);
if (empty($previewToken)) {
return false;
}
$SQL = SQL::newSelect('preview_share');
$SQL->addSelect('preview_share_uri');
$SQL->addWhereOpr('preview_share_expire', date('Y-m-d H:i:s', REQUEST_TIME), '>=');
$SQL->addWhereOpr('preview_share_token', $previewToken);
$url = DB::query($SQL->get(dsn()), 'one');
if (empty($url)) {
return false;
}
return $this->shareUrlFormat(REQUEST_URL) === $url || (is_ajax() && $this->shareUrlFormat(htmlspecialchars_decode(REFERER, ENT_QUOTES)) === $url);
}
/**
* プレビュー共有URLの取得
*
* @param string $url
* @return string
*/
public function getShareUrl($url)
{
$token = uniqueString() . uniqueString();
$SQL = SQL::newInsert('preview_share');
$SQL->addInsert('preview_share_uri', $this->shareUrlFormat($url));
$SQL->addInsert('preview_share_expire', date('Y-m-d H:i:s', REQUEST_TIME + $this->lifetime));
$SQL->addInsert('preview_share_token', $token);
DB::query($SQL->get(dsn()), 'exec');
return $this->shareUrl . "?token={$token}";
}
/**
* 共有URLで実際に表示するiFrameのURL
*
* @return string
*/
public function getSharePreviewUrl()
{
$token = $this->get->get('token');
if (empty($token)) {
throw new \RuntimeException('Empty token');
}
$SQL = SQL::newSelect('preview_share');
$SQL->addSelect('preview_share_uri');
$SQL->addWhereOpr('preview_share_expire', date('Y-m-d H:i:s', REQUEST_TIME), '>=');
$SQL->addWhereOpr('preview_share_token', $token);
if ($url = DB::query($SQL->get(dsn()), 'one')) {
$query = parse_url($url, PHP_URL_QUERY);
if ($query) {
$url .= "&preview-token={$token}";
} else {
$url .= "?preview-token={$token}";
}
return $url;
}
throw new \RuntimeException('Failed get preview url.');
}
/**
* 期限切れの共有URLを削除
*/
public function expiredShareUrl()
{
$SQL = SQL::newDelete('preview_share');
$SQL->addWhereOpr('preview_share_expire', date('Y-m-d H:i:s', REQUEST_TIME - 1), '<');
DB::query($SQL->get(dsn()), 'exec');
}
/**
* プレビューモードを開始
*
* @param string $fakeUserAgent
* @param string $token
* @return bool
*/
public function startPreviewMode($fakeUserAgent, $token)
{
if ($session = $this->getSession()) {
if ($fakeUserAgent && $token) {
$session->set($this->previewFakeUaKeyName, $fakeUserAgent);
$session->set($this->previewFakeUaTokenKeyName, $token);
} else {
$session->delete($this->previewFakeUaKeyName);
$session->delete($this->previewFakeUaTokenKeyName);
}
$session->save();
}
}
/**
* プレビューモードを終了
*
* @return bool
*/
public function endPreviewMode()
{
if ($session = $this->getSession()) {
$session->delete($this->previewFakeUaKeyName);
$session->delete($this->previewFakeUaTokenKeyName);
$session->save();
}
}
/**
* 共有URLから余分な文字列を削除
*
* @param string $url
* @return string
*/
protected function shareUrlFormat($url)
{
$url = preg_replace('/(\?|&|&amp;)(acms-preview-mode|timestamp|preview-token)=[^&]+/', '', $url);
return htmlspecialchars_decode($url, ENT_COMPAT);
}
/**
* @return bool|mixed
*/
private function getSession()
{
if ($this->session) {
return $this->session;
}
if (sessionWithContribution() || ACMS_POST === 'Preview_Mode' || isset($_GET['acms-preview-mode'])) {
$this->session = ACMS_Session::singleton(array(
'sess_storage' => 'acms_preview',
));
return $this->session;
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment