Last active
February 9, 2024 22:05
-
-
Save jakejackson1/49953062700e0ef0a542958cca18078e to your computer and use it in GitHub Desktop.
Alias [gvlogic] 1000 times for deep nesting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Plugin Name: Alias [gvlogic] 1000 times for deep nesting | |
* Description: Because we can (but really shouldn't) | |
* License:GPLv2 | |
*/ | |
add_action( 'plugins_loaded', function () { | |
if ( ! class_exists( '\GV\Shortcodes\gvlogic' ) ) { | |
return; | |
} | |
remove_action( 'init', [ '\GV\Shortcodes\gvlogic', 'add' ] ); | |
add_action( 'init', [ 'gvlogicExtended', 'add' ] ); | |
class gvlogicExtended extends \GV\Shortcode { | |
/** | |
* {@inheritDoc} | |
*/ | |
public $name = 'gvlogic'; | |
/** | |
* {@inheritDoc} | |
*/ | |
public static function add( $name = null ) { | |
parent::add(); // Me, myself and... | |
/** | |
* ...some A LOT OF aliases. | |
*/ | |
for ( $i = 2; $i <= 1000; $i++ ) { | |
parent::add( 'gvlogic' . $i ); | |
} | |
parent::add( 'gvlogicelse' ); | |
add_filter( 'no_texturize_shortcodes', [ __CLASS__, 'filter_no_texturize_shortcodes' ] ); | |
} | |
/** | |
* Process and output the [gvfield] shortcode. | |
* | |
* @param array $atts The attributes passed. | |
* @param string $content The content inside the shortcode. | |
* @param string $tag The tag. | |
* | |
* @return string The output. | |
*/ | |
public function callback( $atts, $content = '', $tag = '' ) { | |
$request = gravityview()->request; | |
$atts = $this->parse_atts( $atts, $content, $tag ); | |
$content = \GravityView_Merge_Tags::replace_get_variables( $content ); | |
$atts = gv_map_deep( $atts, [ '\GravityView_Merge_Tags', 'replace_get_variables' ] ); | |
$content = \GFCommon::replace_variables_prepopulate( $content ); | |
$atts = gv_map_deep( $atts, [ '\GFCommon', 'replace_variables_prepopulate' ] ); | |
// An invalid operation | |
if ( is_null( \GV\Utils::get( $atts, 'logged_in', null ) ) && false === \GV\Utils::get( $atts, 'if', false ) ) { | |
gravityview()->log->error( '$atts->if/logged_in is empty.', [ 'data' => $atts ] ); | |
return apply_filters( 'gravityview/shortcodes/gvlogic/output', '', $atts ); | |
} | |
$authed = $this->authorized( $atts ); | |
$operator = $this->get_operator( $atts ); | |
$value = $this->get_value( $atts ); | |
if ( false === $operator && is_null( $value ) ) { | |
if ( false !== $atts['if'] ) { // Only-if test | |
$match = $authed && ! in_array( strtolower( $atts['if'] ), [ '', '0', 'false', 'no' ] ); | |
} else { | |
$match = $authed; // Just login test | |
} | |
$output = $this->get_output( $match, $atts, $content ); | |
} else { // Regular test | |
$output = $content; | |
// Allow checking against multiple values at once | |
$and_values = explode( '&&', $value ); | |
$or_values = explode( '||', $value ); | |
// Cannot combine AND and OR | |
if ( sizeof( $and_values ) > 1 ) { | |
// Need to match all AND | |
foreach ( $and_values as $and_value ) { | |
$match = $authed && \GVCommon::matches_operation( $atts['if'], $and_value, $operator ); | |
if ( ! $match ) { | |
break; | |
} | |
} | |
} elseif ( sizeof( $or_values ) > 1 ) { | |
// Only need to match a single OR | |
foreach ( $or_values as $or_value ) { | |
$match = \GVCommon::matches_operation( $atts['if'], $or_value, $operator ); | |
// Negate the negative operators | |
if ( ( $authed && $match ) || ( $authed && ( ! $match && in_array( $operator, [ 'isnot', 'not_contains', 'not_in' ] ) ) ) ) { | |
break; | |
} | |
} | |
} else { | |
$match = $authed && \GVCommon::matches_operation( $atts['if'], $value, $operator ); | |
} | |
$output = $this->get_output( $match, $atts, $output ); | |
} | |
// Output and get recursive! | |
$output = do_shortcode( $output ); | |
$output = \GFCommon::replace_variables( $output, [], [], false, true, false ); | |
return apply_filters( 'gravityview/shortcodes/gvlogic/output', $output, $atts ); | |
} | |
/** | |
* Are we authorized to follow the if path? | |
* | |
* @param array $atts The attributes. | |
* | |
* @return bool Yes, or no. | |
*/ | |
private function authorized( $atts ) { | |
$needs_login = \GV\Utils::get( $atts, 'logged_in', null ); | |
if ( is_null( $needs_login ) ) { | |
return true; // No auth requirements have been set | |
} | |
return ! $needs_login ^ is_user_logged_in(); // XNOR | |
} | |
/** | |
* Fetch the operator. | |
* | |
* @param array $atts The attributes. | |
* | |
* @return bool|string The operator. | |
*/ | |
private function get_operator( $atts ) { | |
$valid_ops = $this->get_operators( false ); | |
foreach ( $atts as $op => $value ) { | |
if ( in_array( $op, [ 'if', 'else' ] ) ) { | |
continue; | |
} | |
if ( in_array( $op, $valid_ops, true ) ) { | |
return $op; | |
} | |
} | |
return false; | |
} | |
/** | |
* Fetch the value. | |
* | |
* @param array $atts The attributes. | |
* | |
* @return null|string The value. | |
*/ | |
private function get_value( $atts ) { | |
$valid_ops = $this->get_operators( false ); | |
foreach ( $atts as $op => $value ) { | |
if ( in_array( $op, [ 'if', 'else' ] ) ) { | |
continue; | |
} | |
if ( in_array( $op, $valid_ops, true ) ) { | |
return $value; | |
} | |
} | |
return null; | |
} | |
/** | |
* Get the output content. | |
* | |
* @param bool $match if or else? | |
* @param array $atts The attributes. | |
* @param string $content The content. | |
* | |
* @return string The output. | |
*/ | |
private function get_output( $match, $atts, $content ) { | |
if ( ! $match && ! empty( $atts['else'] ) ) { | |
return $atts['else']; // Attributized else is easy :) | |
} | |
$if = ''; | |
$else = ''; | |
$opens = 0; // inner opens | |
$found = false; // found split position | |
while ( $content ) { // scan | |
if ( ! preg_match( '#(.*?)(\[\/?(gvlogic|else).*?])(.*)#s', $content, $matches ) ) { | |
if ( ! $found ) { // We're still iffing. | |
$if .= $content; | |
} else { // We are elsing | |
$else .= $content; | |
} | |
break; // No more shortcodes | |
} | |
[ $_, $before_shortcode, $shortcode, $_, $after_shortcode ] = $matches; | |
if ( ! $found ) { // We're still iffing. | |
$if .= $before_shortcode; | |
} else { // We are elsing | |
$else .= $before_shortcode; | |
} | |
if ( 0 === strpos( $shortcode, '[else]' ) && 0 === $opens ) { | |
// This is the else we need! | |
$found = true; | |
if ( $match ) { | |
break; // We just need the if on a match, no need to analyze further | |
} | |
} elseif ( $match && 0 === strpos( $shortcode, '[else if' ) && 0 === $opens ) { | |
$found = true; // We found a match, do not process further | |
break; | |
} else { | |
// Increment inner tracking counters | |
if ( 0 === strpos( $shortcode, '[gvlogic' ) ) { | |
$opens++; | |
} | |
if ( 0 === strpos( $shortcode, '[/gvlogic' ) ) { | |
$opens--; | |
} | |
// Tack on the shortcode | |
if ( ! $found ) { // We're still iffing. | |
$if .= $shortcode; | |
} else { // We are elsing | |
$else .= $shortcode; | |
} | |
} | |
$content = $after_shortcode; | |
} | |
gravityview()->log->debug( '[gvlogic] output parsing:', [ | |
'data' => [ | |
'if' => $if, | |
'else' => $else, | |
], | |
] ); | |
if ( ! $match ) { | |
while ( ( $position = strpos( $if, '[else if=' ) ) !== false ) { | |
// Try to match one of the elseif's | |
$sentinel = wp_generate_password( 32, false ); | |
$if = substr( $if, $position ); // ...by replacing it with a gvlogic shortcode | |
// ..and executing it! | |
$result = do_shortcode( preg_replace( '#\[else if#', '[gvlogic if', $if, 1 ) . "[else]{$sentinel}[/gvlogic]" ); | |
if ( $result !== $sentinel ) { | |
// We have an elseif match! | |
return $result; | |
} | |
$if = substr( $if, 1 ); // Move over to get the next elseif match.. and repeat | |
} | |
} | |
return $match ? $if : $else; | |
} | |
/** | |
* Get array of supported operators | |
* | |
* @param bool $with_values | |
* | |
* @return array | |
*/ | |
private function get_operators( $with_values = false ) { | |
$operators = [ | |
'is', | |
'isnot', | |
'contains', | |
'starts_with', | |
'ends_with', | |
'greater_than', | |
'less_than', | |
'in', | |
'not_in', | |
'contains', | |
'equals', | |
'greater_than_or_is', | |
'greater_than_or_equals', | |
'less_than_or_is', | |
'less_than_or_equals', | |
'not_contains', | |
]; | |
if ( $with_values ) { | |
return array_combine( | |
$operators, | |
array_fill( 0, count( $operators ), '' ) | |
); | |
} | |
return $operators; | |
} | |
/** | |
* Process the attributes passed to the shortcode. Make sure they're valid | |
* | |
* @return array Array of attributes parsed for the shortcode | |
*/ | |
private function parse_atts( $atts, $content, $tag ) { | |
$supplied_atts = ! empty( $atts ) ? $atts : []; | |
$atts = shortcode_atts( [ | |
'if' => null, | |
'else' => null, | |
'logged_in' => null, | |
] + $this->get_operators( true ), $atts, $tag ); | |
// Only keep the passed attributes after making sure that they're valid pairs | |
$atts = array_intersect_key( $supplied_atts, $atts ); | |
// Strip whitespace if it's not default false | |
if ( isset( $atts['if'] ) && is_string( $atts['if'] ) ) { | |
$atts['if'] = trim( $atts['if'] ); | |
} else { | |
$atts['if'] = false; | |
} | |
if ( isset( $atts['logged_in'] ) ) { | |
// Truthy | |
if ( in_array( strtolower( $atts['logged_in'] ), [ '0', 'false', 'no' ] ) ) { | |
$atts['logged_in'] = false; | |
} else { | |
$atts['logged_in'] = true; | |
} | |
} | |
/** | |
* @filter `gravityview/gvlogic/atts` The logic attributes. | |
* | |
* @param array $atts The logic attributes. | |
* | |
* @since 2.5 | |
* | |
*/ | |
return apply_filters( 'gravityview/gvlogic/atts', $atts ); | |
} | |
/** | |
* Fixes formatting issues when embedding in posts/pages. | |
* | |
* @see https://github.com/GravityKit/GravityView/issues/1846 | |
* | |
* @since 2.17.6 | |
* | |
* @param array $atts | |
* | |
* @return array | |
*/ | |
public static function filter_no_texturize_shortcodes( $shortcodes = [] ) { | |
$shortcodes[] = 'gvlogic'; | |
$shortcodes[] = 'gvlogicelse'; | |
for ( $i = 2; $i <= 1000; $i++ ) { | |
parent::add( 'gvlogic' . $i ); | |
} | |
return $shortcodes; | |
} | |
} | |
} ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment