diff --git a/docs/testdox.txt b/docs/testdox.txt
index bf966e2ef..d38f5a66e 100644
--- a/docs/testdox.txt
+++ b/docs/testdox.txt
@@ -1912,6 +1912,10 @@ Quick (s9e\TextFormatter\Tests\Configurator\RendererGenerators\PHP\Quick)
[x] Source with data set #22
[x] Source with data set #23
[x] Source with data set #24
+ [x] Source with data set #25
+ [x] Source with data set #26
+ [x] Source with data set #27
+ [x] Source with data set #28
[x] Renders plain text
[x] Renders multi-line text
[x] Renders rich text
diff --git a/src/Bundles/MediaPack/Renderer.php b/src/Bundles/MediaPack/Renderer.php
index e29925f6b..ea40f0e17 100644
--- a/src/Bundles/MediaPack/Renderer.php
+++ b/src/Bundles/MediaPack/Renderer.php
@@ -26,7 +26,7 @@ protected function renderNode(\DOMNode $node)
protected function renderQuickTemplate($id, $xml)
{
$attributes=$this->matchAttributes($xml);
- $html='';switch($id){case'APPLEPODCASTS':$attributes+=['country'=>null,'podcast_id'=>null];$html.='';break;case'AUDIOMACK':$attributes+=['mode'=>null,'artist'=>null,'title'=>null];$html.='';break;case'AUDIUS':$attributes+=['playlist_id'=>null];$html.='';break;case'BANDCAMP':$attributes+=['track_num'=>null,'track_id'=>null];$html.='';break;case'BBCNEWS':$attributes+=['playlist'=>null,'id'=>null];$html.='';break;case'BRIGHTCOVE':$attributes+=['bcpid'=>null,'bctid'=>null];$html.='';break;case'CBSNEWS':$attributes+=['id'=>null];$html.='';elseif(isset($attributes['pid']))$html.=' style="display:block;overflow:hidden;position:relative;padding-bottom:62.1875%;padding-bottom:calc(56.25% + 38px)">';else$html.=' style="display:block;overflow:hidden;position:relative;padding-bottom:62.5%;padding-bottom:calc(56.25% + 40px)">';$html.='';break;case'DAILYMOTION':$attributes+=['id'=>null];$html.='';break;case'DEMOCRACYNOW':$attributes+=['id'=>null];$html.='';break;case'DUMPERT':$attributes+=['id'=>null];$html.='';break;case'FACEBOOK':$attributes+=['type'=>null,'pfbid'=>null];$html.='';break;case'FALSTAD':$attributes+=['ctz'=>null];$html.='';break;case'GETTY':$attributes+=['width'=>null,'height'=>null,'id'=>null,'et'=>null,'sig'=>null];$html.='';break;case'GIFS':$attributes+=['width'=>null,'height'=>null,'id'=>null];$html.='';break;case'GIPHY':$attributes+=['width'=>null,'height'=>null,'id'=>null,'type'=>null];$html.='';break;case'GOOGLEPLUS':$attributes+=['name'=>null,'pid'=>null];$html.='';break;case'GOOGLESHEETS':$attributes+=['type'=>null,'id'=>null,'oid'=>null,'gid'=>null];if($attributes['type']==='chart')$html.='';else{$html.='';}break;case'HUDL':$attributes+=['athlete'=>null,'highlight'=>null];$html.='';break;case'IMGUR':$attributes+=['type'=>null,'id'=>null];$html.='';break;case'INSTAGRAM':$attributes+=['id'=>null];$html.='';break;case'INTERNETARCHIVE':$attributes+=['id'=>null,'width'=>null,'height'=>null];if(str_contains($attributes['id']??'','playlist=1'))$html.='';else{$html.='';}break;case'JSFIDDLE':$attributes+=['id'=>null,'revision'=>null];$html.='';break;case'KALTURA':$attributes+=['partner_id'=>null,'uiconf_id'=>null,'entry_id'=>null];$html.='';break;case'KICKSTARTER':$attributes+=['id'=>null];$html.='';else$html.=' style="display:inline-block;width:100%;max-width:220px">';$html.='';break;case'LIVESTREAM':$attributes+=['channel'=>null,'account_id'=>null,'event_id'=>null];$html.='';break;case'MASTODON':$attributes+=['name'=>null,'id'=>null];$html.='';break;case'MEGAPHONE':$attributes+=['id'=>null];$html.='';break;case'MIXCLOUD':$attributes+=['id'=>null];$html.='';break;case'MSNBC':$attributes+=['id'=>null];$html.='';break;case'NHL':$html.='';break;case'ODYSEE':$attributes+=['name'=>null,'id'=>null,'path'=>null];$html.='';break;case'ORFIUM':$attributes+=['track_id'=>null];$html.='';break;case'PINTEREST':$attributes+=['id'=>null];$html.='';break;case'REDDIT':$attributes+=['id'=>null,'path'=>null];$html.='';break;case'SOUNDCLOUD':$attributes+=['secret_token'=>null,'id'=>null];$html.='';break;case'SPOTIFY':$attributes+=['id'=>null,'path'=>null];$html.='';break;case'SPREAKER':$attributes+=['episode_id'=>null,'show_id'=>null];$html.='';break;case'TED':$attributes+=['id'=>null];$html.='';break;case'TELEGRAM':$attributes+=['id'=>null];$html.='';break;case'TRADINGVIEW':$attributes+=['chart'=>null];$html.='';break;case'TWENTYFOURSEVENSPORTS':$attributes+=['player_id'=>null];if(isset($attributes['video_id']))$html.='';else$html.='';break;case'TWITTER':$attributes+=['id'=>null];$html.='';break;case'USTREAM':$attributes+=['cid'=>null];$html.='';break;case'VIMEO':$attributes+=['id'=>null];$html.='';break;case'WAVEKIT':$attributes+=['audio_id'=>null];$html.='';break;case'WISTIA':$attributes+=['type'=>null,'id'=>null];if($attributes['type']==='audio')$html.='';else$html.='';break;case'XENFORO':$attributes+=['url'=>null,'thread_id'=>null];$html.='';break;case'YOUTUBE':$attributes+=['id'=>null,'clipt'=>null,'t'=>null];$html.='';}
+ $html='';switch($id){case'APPLEPODCASTS':$attributes+=['country'=>null,'podcast_id'=>null];$html.='';break;case'AUDIOMACK':$attributes+=['mode'=>null,'artist'=>null,'title'=>null];$html.='';break;case'AUDIUS':$attributes+=['playlist_id'=>null];$html.='';break;case'BANDCAMP':$attributes+=['track_num'=>null,'track_id'=>null];$html.='';break;case'BBCNEWS':$attributes+=['playlist'=>null,'id'=>null];$html.='';break;case'BRIGHTCOVE':$attributes+=['bcpid'=>null,'bctid'=>null];$html.='';break;case'CBSNEWS':$attributes+=['id'=>null];$html.='';elseif(isset($attributes['pid']))$html.=' style="display:block;overflow:hidden;position:relative;padding-bottom:62.1875%;padding-bottom:calc(56.25% + 38px)">';else$html.=' style="display:block;overflow:hidden;position:relative;padding-bottom:62.5%;padding-bottom:calc(56.25% + 40px)">';$html.='';break;case'DAILYMOTION':$attributes+=['id'=>null];$html.='';break;case'DEMOCRACYNOW':$attributes+=['id'=>null];$html.='';break;case'DUMPERT':$attributes+=['id'=>null];$html.='';break;case'FACEBOOK':$attributes+=['type'=>null,'pfbid'=>null];$html.='';break;case'FALSTAD':$attributes+=['ctz'=>null];$html.='';break;case'GETTY':$attributes+=['width'=>null,'height'=>null,'id'=>null,'et'=>null,'sig'=>null];$html.='';break;case'GIFS':$attributes+=['width'=>null,'height'=>null,'id'=>null];$html.='';break;case'GIPHY':$attributes+=['width'=>null,'height'=>null,'id'=>null,'type'=>null];$html.='';break;case'GOOGLEPLUS':$attributes+=['name'=>null,'pid'=>null];$html.='';break;case'GOOGLESHEETS':$attributes+=['type'=>null,'id'=>null,'oid'=>null,'gid'=>null];if($attributes['type']==='chart')$html.='';else{$html.='';}break;case'HUDL':$attributes+=['athlete'=>null,'highlight'=>null];$html.='';break;case'IMGUR':$attributes+=['type'=>null,'id'=>null];$html.='';break;case'INSTAGRAM':$attributes+=['id'=>null];$html.='';break;case'INTERNETARCHIVE':$attributes+=['id'=>null,'width'=>null,'height'=>null];if(str_contains($attributes['id']??'','playlist=1'))$html.='';else{$html.='';}break;case'JSFIDDLE':$attributes+=['id'=>null,'revision'=>null];$html.='';break;case'KALTURA':$attributes+=['partner_id'=>null,'uiconf_id'=>null,'entry_id'=>null];$html.='';break;case'KICKSTARTER':$attributes+=['id'=>null];$html.='';else$html.=' style="display:inline-block;width:100%;max-width:220px">';$html.='';break;case'LIVESTREAM':$attributes+=['channel'=>null,'account_id'=>null,'event_id'=>null];$html.='';break;case'MASTODON':$attributes+=['name'=>null,'id'=>null];$html.='';break;case'MEGAPHONE':$attributes+=['id'=>null];$html.='';break;case'MIXCLOUD':$attributes+=['id'=>null];$html.='';break;case'MSNBC':$attributes+=['id'=>null];$html.='';break;case'NHL':$html.='';break;case'ODYSEE':$attributes+=['name'=>null,'id'=>null,'path'=>null];$html.='';break;case'ORFIUM':$attributes+=['track_id'=>null];$html.='';break;case'PINTEREST':$attributes+=['id'=>null];$html.='';break;case'REDDIT':$attributes+=['id'=>null,'path'=>null];$html.='';break;case'SOUNDCLOUD':$attributes+=['secret_token'=>null,'id'=>null];$html.='';break;case'SPOTIFY':$attributes+=['id'=>null,'path'=>null];$html.='';break;case'SPREAKER':$attributes+=['episode_id'=>null,'show_id'=>null];$html.='';break;case'TED':$attributes+=['id'=>null];$html.='';break;case'TELEGRAM':$attributes+=['id'=>null];$html.='';break;case'TRADINGVIEW':$attributes+=['chart'=>null];$html.='';break;case'TWENTYFOURSEVENSPORTS':$attributes+=['player_id'=>null];if(isset($attributes['video_id']))$html.='';else$html.='';break;case'TWITTER':$attributes+=['id'=>null];$html.='';break;case'USTREAM':$attributes+=['cid'=>null];$html.='';break;case'VIMEO':$attributes+=['id'=>null];$html.='';break;case'WAVEKIT':$attributes+=['audio_id'=>null];$html.='';break;case'WISTIA':$attributes+=['type'=>null,'id'=>null];if($attributes['type']==='audio')$html.='';else$html.='';break;case'XENFORO':$attributes+=['url'=>null,'thread_id'=>null];$html.='';break;case'YOUTUBE':$attributes+=['id'=>null,'clipt'=>null,'t'=>null];$html.='';}
return $html;
}
diff --git a/src/Configurator/RendererGenerators/PHP/Quick.php b/src/Configurator/RendererGenerators/PHP/Quick.php
index 296d837b3..c13e9bd65 100644
--- a/src/Configurator/RendererGenerators/PHP/Quick.php
+++ b/src/Configurator/RendererGenerators/PHP/Quick.php
@@ -428,16 +428,16 @@ protected static function replacePHP(&$php)
// A comparison between an attribute and a literal string. Rather than unescape the
// attribute value, we escape the literal. This applies to comparisons using XPath's
// contains() as well (translated to PHP's strpos())
- '(' . $getAttribute . '===(' . $string . '))s'
+ '(' . $getAttribute . '([!=]==)(' . $string . '))s'
=> function ($m)
{
- return '$attributes[' . $m[1] . ']===' . htmlspecialchars($m[2], ENT_COMPAT);
+ return '$attributes[' . $m[1] . ']' . $m[2] . htmlspecialchars($m[3], ENT_COMPAT);
},
- '((' . $string . ')===' . $getAttribute . ')s'
+ '((' . $string . ')([!=]==)' . $getAttribute . ')s'
=> function ($m)
{
- return htmlspecialchars($m[1], ENT_COMPAT) . '===$attributes[' . $m[2] . ']';
+ return htmlspecialchars($m[1], ENT_COMPAT) . $m[2] . '$attributes[' . $m[3] . ']';
},
'(strpos\\(' . $getAttribute . ',(' . $string . ')\\)([!=]==(?:0|false)))s'
@@ -466,11 +466,11 @@ protected static function replacePHP(&$php)
// An attribute value used in an arithmetic comparison or operation does not need to be
// unescaped. The same applies to empty(), isset() and conditionals
- '(' . $getAttribute . '(?=(?:==|[-+*])\\d+))' => '$attributes[$1]',
- '(\\b(\\d+(?:==|[-+*]))' . $getAttribute . ')' => '$1$attributes[$2]',
- '(empty\\(' . $getAttribute . '\\))' => 'empty($attributes[$1])',
- "(\\\$node->hasAttribute\\(('[^']+')\\))" => 'isset($attributes[$1])',
- 'if($node->attributes->length)' => 'if($this->hasNonNullValues($attributes))',
+ '(' . $getAttribute . '(?=(?:[!=]=|[-+*])\\d+))' => '$attributes[$1]',
+ '(\\b(\\d+(?:[!=]=|[-+*]))' . $getAttribute . ')' => '$1$attributes[$2]',
+ '(empty\\(' . $getAttribute . '\\))' => 'empty($attributes[$1])',
+ "(\\\$node->hasAttribute\\(('[^']+')\\))" => 'isset($attributes[$1])',
+ 'if($node->attributes->length)' => 'if($this->hasNonNullValues($attributes))',
// In all other situations, unescape the attribute value before use
'(' . $getAttribute . ')' => 'htmlspecialchars_decode($attributes[$1]??\'\')'
diff --git a/tests/Configurator/RendererGenerators/PHP/QuickTest.php b/tests/Configurator/RendererGenerators/PHP/QuickTest.php
index d976f671b..ee1a5e512 100644
--- a/tests/Configurator/RendererGenerators/PHP/QuickTest.php
+++ b/tests/Configurator/RendererGenerators/PHP/QuickTest.php
@@ -689,10 +689,18 @@ public static function getSourceTests()
['X' => 'x'],
"if(\$attributes['x']==1){\$html.='x';}"
],
+ [
+ ['X' => 'x'],
+ "if(\$attributes['x']!=1){\$html.='x';}"
+ ],
[
['X' => 'x'],
"if(1==\$attributes['x']){\$html.='x';}"
],
+ [
+ ['X' => 'x'],
+ "if(1!=\$attributes['x']){\$html.='x';}"
+ ],
[
['X' => '
'],
"\$attributes['x']+200*\$attributes['y']"
@@ -785,6 +793,14 @@ public static function getSourceTests()
['X' => 'Y'],
'if($this->hasNonNullValues($attributes)){$html.=\'Y\';}'
],
+ [
+ ['X' => 'X'],
+ "if(\$attributes['foo']!==''){\$html.='X';}"
+ ],
+ [
+ ['X' => 'X'],
+ "if(''!==\$attributes['foo']){\$html.='X';}"
+ ],
];
}
}
\ No newline at end of file