title: PHP Encode parts of a string and then decode
body: <p>I have a string containing HTML and some placeholders.</p>
  
  <p>The placeholders always start with <code>{{</code> and end with <code>}}</code>.</p>
  
  <p>I'm trying to encode the contents of places holders and the decode them later.</p>
  
  <p>While they're encoded the ideally need to be valid HTML as I want to use DOMDocument on the string and the problem I'm having is that it ends up being a mess because the places holders are usually something like:</p>
  
  <pre><code>&lt;img src="{{image url="mediadir/someimage.jpg"}}"/&gt;
  </code></pre>
  
  <p>Sometimes they're something like this though:</p>
  
  <pre><code>&lt;p&gt;Some text&lt;/p&gt;
  {{widget type="pagelink" pageid="1"}}
  &lt;div class="whatever"&gt;Content&lt;/div&gt;
  </code></pre>
  
  <p>I was wondering what the best way of doing this, thanks!</p>
  
  <p>UPDATE: CONTEXT</p>
  
  <p>The overall problem is that I have Magento site with a bunch of static links like:</p>
  
  <pre><code>&lt;a href="/some/pageurl"&gt;Link text&lt;/a&gt;
  </code></pre>
  
  <p>And I need to replace them with widgets to the page so that if the URL changes the links update. So replace the above with something like this.</p>
  
  <pre><code>{{widget type="Magento\Cms\Block\Widget\Page\Link" anchor_text="Link Text" template="widget/link/link_block.phtml" page_id="123"}}
  </code></pre>
  
  <p>I have something which does this using the PHP DOMDocument functionality. It looks up CMS page through their URL, finds the ID and replaces the anchor node with the widget text. This works fine if the page doesn't already contain any widgets or URL placeholders.</p>
  
  <p>However if it does then the placeholders come out broken when processed through the DOMDocument saveHTML() function.</p>
  
  <p>My idea of a solution to this was to encode the widgets and URL placeholders before passing it toe DOMDocument loadHTML() function and decode them after the saveHTML() function when it is string again.</p>
  
  <p>UPDATE: CODE</p>
  
  <p>This is a cut down version of what I've got currently. It's messy but it does work in replacing pages with widgets.</p>
  
  <pre><code>$pageCollection = $this-&gt;pageCollectionFactory-&gt;create();
  
  $collection = $pageCollection-&gt;load();
  
  $findarray = array('http', 'mailto', '.pdf', '{', '}');
  $findarray2 = array('mailto', '.pdf', '{', '}');
  $specialurl = 'https://www.example.com';
  $relative_links = 0;
  $missing_pages = 0;
  $fixed_links = 0;
  
  try {
      foreach ($collection as $page) {
  
      $dom = new \DOMDocument();
  
      $content = $this-&gt;cleanMagentoCode( $page-&gt;getContent() );
      libxml_use_internal_errors(true); // Surpress warnings created by reading bad HTML
      $dom-&gt;loadHTML( $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); // Load HTML without doctype or html containing elements
  
      $elements = $dom-&gt;getElementsByTagName("a");
      for ($i = $elements-&gt;length - 1; $i &gt;= 0; $i --) {
          $link = $elements-&gt;item($i);
  
          $found = false;
  
          // To clean up later
          if ( strpos($link-&gt;getAttribute('href'), $specialurl) !== FALSE ) {
              foreach ($findarray2 as $find) {
                  if (stripos($link-&gt;getAttribute('href'), $find) !== FALSE) {
                      $found = true;
                      break;
                  }
              }
          } else {
              foreach ($findarray as $find) {
                  if (stripos($link-&gt;getAttribute('href'), $find) !== FALSE) {
                      $found = true;
                      break;
                  }
              }
          }
  
          if ( strpos($link-&gt;getAttribute('href'), '#') === 0 ) {
              $found = true;
          }
  
          if ( $link-&gt;getAttribute('href') == '' ) {
              $found = true;
          }
  
          if ( !$found ) {
              $url = parse_url($link-&gt;getAttribute('href'));
              if ( isset( $url['path'] ) ) {
                  $identifier = rtrim( ltrim($url['path'], '/'), '/' );
  
                  try {
                      $pagelink = $this-&gt;pageRepository-&gt;getById($identifier);
  
                      // Fix link
                      if ($this-&gt;fixLinksFlag($input)) {
                          if ( stripos( $link-&gt;getAttribute('class'), "btn" ) !== FALSE ) {
                              $link_template = "widget/link/link_block.phtml";
                          } else {
                              $link_template = "widget/link/link_inline.phtml";
                          }
  
                          $widgetcode = '{{widget type="Magento\Cms\Block\Widget\Page\Link" anchor_text="' . $link-&gt;nodeValue . '" template="' . $link_template . '" page_id="' . $pagelink-&gt;getId() . '"}}';
                          $widget = $dom-&gt;createTextNode($widgetcode);
  
                          $link-&gt;parentNode-&gt;replaceChild($widget, $link);
                      }
                  }
              }
          }
      }
  
      $page-&gt;setContent( $this-&gt;dirtyMagentoCode( $dom-&gt;saveHTML() ) );
      $page-&gt;save();
      }
  }
  </code></pre>