HTML Purifier 機会の拡大



ほんの数段落で、このライブラリとYiiフレームワークの相互作用の機能に注意を払います。残りは完全に普遍的であり、このライブラリを使用または使用する予定のあるすべての人にとって興味深いものです。



既にPurifierに精通している場合は、ここから安全に読み始めることができます。



HTML Purifierについて


HTML Purifierのような美しいライブラリ(およびHabréでの検索ではあまり人気がない)について聞いたことがない場合は、特にユーザーがhtml形式でコンテンツを生成する場合は、詳しく調べることをお勧めします。 これは、一般ユーザー、モデレーター、または管理者である場合があります。

このライブラリは何をしますか?

構成に従って、個々の属性を含む、コードのすべての悪意のある、無効な、禁止された(構成により)部分からすべてのhtmlコードをクリアします。



単語を減らし、コードを増やします。


いくつかの例は、それ自体を物語っていると思う。

$config = HTMLPurifier_Config::createDefault(); $config->set('Attr.AllowedClasses',array('header')); //  Attr.ForbiddenClasses   CSS  $config->set('AutoFormat.AutoParagraph',true); //   <p>     $config->set('AutoFormat.RemoveEmpty',true); //   ,  * $config->set('HTML.Doctype','HTML 4.01 Strict'); //      <strike> $purifier = new HTMLPurifier($config); $clean_html = $purifier->purify($html);
      
      





* -RemoveEmpty例外



ソースHTML:

  <p invalidAttribute="value">,    <strike></strike>:</p> <p>  - <invalidTag></invalidTag>,</p> <p class="header error"> - ,</p>  - ! <script type="text/javascript">alert("hacked by Alexander Blok");</script>
      
      





浄化機能の結果

  <p>,    <span style="text-decoration:line-through;"></span>:</p> <p>  - ,</p> <p class="header"> - ,</p> <p> - !</p>
      
      







設定の数は印象的で、ボックスから必要なパンを入手することができます。





「真珠ボタンの母」


しかし、いつものように特別な何か、つまり次の2つのことを望まない場合、この投稿は存在しません。

  1. 外部サイトへのすべてのリンクをsite.ru/redirect?url=linkという形式のリンクに置き換えます
  2. すべてのユーザーリンクにtarget = _blank属性を追加します


最初はドックに関する優れた記事があり、2番目は一般に些細なことであるHTML構成によると、タスクはそれほど複雑に見えませんでした。TargetBlankが作業を行ってくれます。



タスク1-外部リンクを置き換える


Purifierには、HTMLPurifier_URIFilterというすばらしいクラスがあり、このフィルターの機能を実装するすばらしい例もあります

DisableExternalResourcesファイルを基礎として、必要に応じてすばやく書き直しました。つまり、外部リンクを内部リンクに置き換えました。

フィルターファイル
短い説明:

準備機能では、サイトのホストを取得し、ポイントで分割し、配列を展開します。

その結果、配列(「ru」、「site」、「subdomen」)を取得します。

フィルター関数では、ユーザーリンクで同じことを行い、ホストを比較します。同じ場合は何も変更せず、trueを返します。そうでない場合は、アドレスで新しいURIオブジェクトを作成し、GETパラメーターにユーザーリンクを挿入します。

重要 filterメソッドは、trueまたはfalse以外を返さないでください。 return経由でリンクを返すことでリンクを置き換えようとしないでください。

 <?php class HTMLPurifier_URIFilter_MakeRedirect extends HTMLPurifier_URIFilter { /** * @type string */ public $name = 'MakeRedirect'; /** * @type array */ protected $ourHostParts = false; /** * @param HTMLPurifier_Config $config * @return void */ public function prepare($config) { $our_host = $config->getDefinition('URI')->host; if ($our_host !== null) { $this->ourHostParts = array_reverse(explode('.', $our_host)); } } /** * @param HTMLPurifier_URI $uri Reference * @param HTMLPurifier_Config $config * @param HTMLPurifier_Context $context * @return bool */ public function filter(&$uri, $config, $context) { if (is_null($uri->host)) { return true; } if ($this->ourHostParts === false) { return false; } $host_parts = array_reverse(explode('.', $uri->host)); foreach ($this->ourHostParts as $i => $x) { if (!isset($host_parts[$i]) || $host_parts[$i] != $this->ourHostParts[$i]) { $path = Yii::app()->createUrl('site/redirect'); //  Yii,      url manager       /action,    $query = 'url='.urlencode($uri->toString()); $uri = new HTMLPurifier_URI('http', null, Yii::app()->request->getServerName(), // return $_SERVER['SERVER_NAME'] null, $path, $query, null); break; } } return true; } }
      
      







フィルターを適用


このため、ドキュメントが示唆しているように、HTMLPurifier_Configオブジェクトに目を向ける必要があります。

  $config = HTMLPurifier_Config::createDefault(); $uri = $config->getDefinition('URI'); $uri->addFilter(new HTMLPurifier_URIFilter_MakeRedirect(), $config); $purifier = new HTMLPurifier($config); $clean_html = $purifier->purify($html);
      
      





幸せなYiiユーザーのための段落


私はそれらの1人です( 後悔はしていません )。 Yii はすぐに Purifierをサポートしますが、すべてがスムーズではありません。

ドキュメントの例:

 $p = new CHtmlPurifier(); //   Yii $p->options = array('URI.AllowedSchemes'=>array('http' => true, 'https' => true,)); //      $text = $p->purify($text);
      
      





そこから学習します。

  /** * @var mixed the options to be passed to HTML Purifier instance. * This can be a HTMLPurifier_Config object, an array of directives (Namespace.Directive => Value) * or the filename of an ini file. * @see http://htmlpurifier.org/live/configdoc/plain.html */ private $_options=null;
      
      





すべてがうまくいくようです。配列の代わりにHTMLPurifier_Configオブジェクトを渡すことができます。試してください:

  $purifier = new CHtmlPurifier(); $config = HTMLPurifier_Config::createDefault(); $config->set('AutoFormat.RemoveEmpty', true); $uri = $config->getDefinition('URI'); $uri->addFilter(new HTMLPurifier_URIFilter_MakeRedirect(), $config); $purifier->options = $config; $clean_html = $purifier->purify($html);
      
      





  Warning Base directory /framework/vendors/htmlpurifier/standalone/HTMLPurifier/DefinitionCache/Serializer does not exist, please create or change using %Cache.SerializerPath
      
      





ここでは、動揺してGoggle CHtmlPurifier マナにクロールせず、Cache.SerializerPathパラメーターに値Yii :: app()-> getRuntimePath()を設定する必要があることがわかります。これにより、このフォルダーを使用してキャッシュを保存できるようになります

私たちはやる:

 $purifier = new CHtmlPurifier(); $config = HTMLPurifier_Config::createDefault(); $config->set('AutoFormat.RemoveEmpty', true); $config->set('Cache.SerializerPath',Yii::app()->getRuntimePath()); // <-- $uri = $config->getDefinition('URI'); $uri->addFilter(new HTMLPurifier_URIFilter_MakeRedirect(), $config); $purifier->options = $config; $clean_html = $purifier->purify($html);
      
      





 Cannot set directive after finalization invoked on line 127 in file /framework/web/widgets/CHtmlPurifier.php
      
      





現在、purrierは、パラメータを2回定義することを好みません。 そして、CHtmlPurifierはcreateNewHtmlPurifierInstance()メソッドでそれを行います

 protected function createNewHtmlPurifierInstance() { $this->_purifier=new HTMLPurifier($this->getOptions()); $this->_purifier->config->set('Cache.SerializerPath',Yii::app()->getRuntimePath()); return $this->_purifier; }
      
      





ここで、私は美しい解決策を探すのに多くの時間を費やしたが、悲しいかな。 createNewHtmlPurifierInstance()メソッドを上書きしてGHtmlPurifierクラスを作成し、CHtmlPurifierクラスから継承することほど美しいものは見つかりませんでした。

新しいファイルをprotected / components / folderに入れて、コードがようやく機能しました。

  $htmlpurifier = new GHtmlPurifier(); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.SerializerPath',Yii::app()->getRuntimePath()); $uri = $config->getDefinition('URI'); $uri->addFilter(new HTMLPurifier_URIFilter_MakeRedirect(), $config); $htmlpurifier->options = $config; return $htmlpurifier->purify($text);
      
      





タスク2-ターゲットの追加= _blank


動作しないコードの例に悩まされることはありません。HTML.TargetBlankは外部リンクでのみ機能し、そのアプリケーションは不要になったとすぐに言います。 また、URIフィルターはタグとその属性にアクセスできません。

すでにライブラリの優れたドキュメントに慣れていた彼はマナに登りましたが、残念ながら、必要なAdvanced APIセクションは空で、 「開発中のファイル」という碑文がそこに表示されていました

ソースに飛び込んで、HTML.TargetBlankモジュールがどのように実装されているかを見つける以外に何も残っていませんでした。

ここにあります:

HTMLPurifier_AttrTransform_TargetBlank
 /** * Adds target="blank" to all outbound links. This transform is * only attached if Attr.TargetBlank is TRUE. This works regardless * of whether or not Attr.AllowedFrameTargets */ class HTMLPurifier_AttrTransform_TargetBlank extends HTMLPurifier_AttrTransform { private $parser; public function __construct() { $this->parser = new HTMLPurifier_URIParser(); } public function transform($attr, $config, $context) { if (!isset($attr['href'])) { return $attr; } // XXX Kind of inefficient $url = $this->parser->parse($attr['href']); $scheme = $url->getSchemeObj($config, $context); if ($scheme->browsable && !$url->isBenign($config, $context)) { $attr['target'] = '_blank'; } return $attr; } }
      
      







外部アドレスのチェックを含まない独自のモジュールを作成することにしましたが、見つかったすべてのリンクにtarget = _blankを追加します。

変換メソッドの2、3行のコピーと削除に誰もが対処できると思います。 したがって、リストしません。 モジュールの名前を変更することを忘れないことが重要です。HTMLPurifier_AttrTransform_TargetBlankAllという名前を付けて、同じフォルダー/ protected / components /に配置します。

しかし、これでは十分ではなく、モジュールは自動的に取得されないため、構成にモジュールを追加するクラスを作成する必要があります。 コードには、独自のモジュールを作成する場合に何を変更する必要があるかが明確であるというコメントをいくつか追加しました。

HTMLPurifier_HTMLModule_TargetBlankAll.php
 class HTMLPurifier_HTMLModule_TargetBlankAll extends HTMLPurifier_HTMLModule { public $name = 'TargetBlankAll'; //      .     public function setup($config) { $a = $this->addBlankElement('a'); // ,        A $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetBlankAll(); //       //      $a->attr_transform_pre[] } }
      
      







また、このファイルを/ protected / componentsフォルダーに入れました。

このモジュールを設定に追加して、結果を楽しむことが今のままです。 それが行われている、これは完全に論理的ではありません。 HTMLオブジェクトへのリンクを取得する必要があり、初期化して__construct()メソッドがHTMLPurifier_HTMLDefinitionクラスで機能するように、パラメーター$ raw = trueが必要です。

__construct()メソッドは、モジュールを接続するために使用する$ this-> manager変数を初期化します。

  $htmlpurifier = new GHtmlPurifier(); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.SerializerPath',Yii::app()->getRuntimePath()); $uri = $config->getDefinition('URI'); $uri->addFilter(new HTMLPurifier_URIFilter_MakeRedirect(), $config); $html = $config->getHTMLDefinition(true); //     HTMLPurifier_HTMLDefinition $html->manager->addModule('TargetBlankAll'); //      $htmlpurifier->options = $config; return $htmlpurifier->purify($text);
      
      





タダム:

 <a href="http://site.ru/">http://site.ru</a> <a href="http://habrahabr.ru/">http://habrahabr.ru</a>
      
      





 <a href="http://site.ru/" target="_blank">http://site.ru</a> <a href="http://site.ru/redirect/?url=http%3A%2F%2Fhabrahabr.ru%2F" target="_blank">http://habrahabr.ru</a>
      
      





両方のタスクが完了しました!




この記事でこの素晴らしいツールを紹介し、同時にサイトをより面白く安全にすることで、ユーザーがhtmlのすべての機能を使用して興味深いコンテンツを作成できるようになることを願っています。



このライブラリは高速ではないため、オンザフライでデータを出力するために使用しないでください。



All Articles