title: Blade::compileString() but return string concatination instead of inline PHP
body: <p>I used <a href="https://laravel.com/api/8.x/Illuminate/View/Compilers/BladeCompiler.html#method_extend" rel="nofollow noreferrer"><code>Blade::extend()</code></a> to create a more complex directive than it is possible with <a href="https://laravel.com/docs/8.x/blade#extending-blade" rel="nofollow noreferrer"><code>Blade::directive()</code></a>. After trying different approaches for hours, this is what I ended up with—and it works. Except for one thing, I'm still not entirely fine with:</p>
  <p>When I pass <code>$matches['callback']</code> (which contains ordinary blade syntax) to the output, Blade converts a string like this:</p>
  <pre><code>&lt;a href=&quot;{{ route('user', [ 'user' =&gt; $user ]) }}&quot;&gt;{{ $user-&gt;username }}&lt;/a&gt;
  </code></pre>
  <p>to</p>
  <pre><code>&lt;a href=&quot;&lt;?php echo e(route('community.users.user', [ 'user' =&gt; $author-&gt;id ])); ?&gt;&quot;&gt;&lt;?php echo e($author-&gt;username); ?&gt;&lt;/a&gt;
  </code></pre>
  <p>This is done, because internally, Blade seems to use <a href="https://laravel.com/api/8.x/Illuminate/View/Compilers/BladeCompiler.html#method_compileString" rel="nofollow noreferrer"><code>Blade::compileString()</code></a>. That might be fine in some cases, but as you can probably see, it breaks the layout, because the whole <code>&lt;a /&gt;</code> tag has to be <code>echo</code>'d and the inner stuff has to be concatenated like this to return valid HTML:</p>
  <pre><code>'&lt;a href=&quot;'. route('community.users.user', [ 'user' =&gt; $author-&gt;id ]) .'&quot;&gt;'. $author-&gt;username .'&lt;/a&gt;'
  </code></pre>
  <p>As you can see, I have a workaround, to manually replace <code>{{</code> and <code>}}</code> so that string concatenation is happening, but I wonder if there's a way, to do that <em>properly</em>.</p>
  <p>AppServiceProvider.php:</p>
  <pre class="lang-php prettyprint-override"><code>Blade::extend(function($expression) {
      $pattern = '/\@mapJoin\((?&lt;collection&gt;.*?), \'(?&lt;variable&gt;.*?)\', (?&lt;delimiter&gt;.*?)\)(?&lt;callback&gt;.*?)\@endMapJoin/ms';
      return preg_replace_callback($pattern, function($matches) {
          // @todo dynamic compiling
          $callback = preg_replace('/\{\{(.*?)\}\}/', &quot;'.$1.'&quot;, trim($matches[&quot;callback&quot;]));
          return '&lt;?php echo '.$matches['collection'].'-&gt;map(function($'.$matches['variable'].') {
              return \''.$callback.'\';
          })-&gt;join('.$matches[&quot;delimiter&quot;].'); ?&gt;'; //Blade::compileString()
      }, $expression);
  });
  </code></pre>
  <hr />
  <p>I wanted to have something in my blade syntax which allows me to do the following:</p>
  <pre><code>@mapJoin($story-&gt;authors, 'author', ',')
      &lt;!-- normal markup and blade stuff --&gt;
  @endMapJoin
  </code></pre>
  <p>for example:</p>
  <pre><code>@mapJoin($story-&gt;authors, 'author', ',')
      &lt;a href=&quot;{{ route('community.users.user', [ 'user' =&gt; $author-&gt;id ]) }}&quot;&gt;{{ $author-&gt;username }}&lt;/a&gt;
  @endMapJoin
  </code></pre>
  <p>to turn it into:</p>
  <pre class="lang-php prettyprint-override"><code>$collection-&gt;map(function($variable) {
      return echo $callback;
  })-&gt;join($delimiter);
  </code></pre>
  <p>which would result in:</p>
  <pre class="lang-html prettyprint-override"><code>&lt;a href=&quot;community/users/user/1&quot;&gt;User 1&lt;/a&gt;, &lt;a href=&quot;community/users/user/2&quot;&gt;User 2&lt;/a&gt;, &lt;a href=&quot;community/users/user/1&quot;&gt;User 2&lt;/a&gt;
  </code></pre>
  <p><code>Blade::directive()</code> sadly only replaces the <code>@something</code> itself, not the content between, and it does not have a concept of start and end tag. I tried to address that with my solution using <code>Blade::extend()</code>. Even more general, it would be something like this:</p>
  <pre class="lang-php prettyprint-override"><code>use Illuminate\Support\Facades\Blade;
  use Illuminate\View\Compilers\BladeCompiler as CompilersBladeCompiler;
  use Illuminate\Support\Str;
  
  class BladeCompiler extends CompilersBladeCompiler
  {
      public static function compileDirective(string $name, array $parameters, callable $callback)
      {
          $paramString = '';
          if (count($parameters)) {
              $paramString = '\(';
              foreach($parameters as $parameter) {
                  $paramString .= '(?&lt;'.$parameter.'&gt;.*?)';
              }
              $paramString .= '\)';
          }
          return Blade::extend(function($expression) use($name, $paramString, $callback) {
              $pattern = '/\@'.$name.$paramString.'(?&lt;content&gt;.*?)\@end'.Str::studly($name).'/ms';
              return preg_replace_callback($pattern, $callback, $expression);
          });
      }
  }
  </code></pre>