/** * Executes PHP code */ public function php() { $this->CI = get_instance(); // Prep our content $content = '<?php' . $this->tag_content . '?>'; // Run our content through the parser $parser = new Lex_Parser(); $parser->scope_glue(':'); $content = $parser->parse($content, $this->CI->vars, array($this->CI->parse, 'callback'), true); return $content; }
/** * Return a parsed embedded template */ public function embed() { $CI = get_instance(); if (!$this->get_param('file')) { return NULL; } $CI->load->helper('file'); // Load the file. Always an .html $embed_content = read_file(FCPATH . $CI->vars['assets_folder'] . '/embeds/' . $this->get_param('file') . '.html'); if (!$embed_content) { return NULL; } // We don't want our file variable // being parsed with the rest. unset($this->attributes['file']); $vars = array_merge($this->attributes, $CI->vars); $parser = new Lex_Parser(); $parser->scope_glue(':'); return $parser->parse($embed_content, $CI->vars, array($CI->parse, 'callback')); }
/** * Markdown the content */ public function format() { $this->CI = get_instance(); $method = $this->get_param('method', 'markdown'); // Prep our content $content = trim($this->tag_content); // Run our content through the parser $parser = new Lex_Parser(); $parser->scope_glue(':'); $content = $parser->parse($content, $this->CI->vars, array($this->CI->parse, 'callback')); switch ($method) { // Textile case 'textile': require_once 'textile.php'; return TextileThis($content); break; // Default is markdown // Default is markdown default: require_once 'markdown.php'; return Markdown($content); break; } }
/** * The main Lex parser method. Essentially acts as dispatcher to * all of the helper parser methods. * * @param string $text Text to parse * @param array|object $data Array or object to use * @param mixed $callback Callback to use for Callback Tags * @return string */ public function parse($text, $data = array(), $callback = false, $allowPhp = false) { $this->setupRegex(); $this->allowPhp = $allowPhp; // Is this the first time parse() is called? if (self::$data === null) { // Let's store the local data array for later use. self::$data = $data; } else { // Let's merge the current data array with the local scope variables // So you can call local variables from within blocks. $data = array_merge(self::$data, $data); // Since this is not the first time parse() is called, it's most definately a callback, // let's store the current callback data with the the local data // so we can use it straight after a callback is called. self::$callbackData = $data; } // The parseConditionals method executes any PHP in the text, so clean it up. if (!$allowPhp) { $text = str_replace(array('<?', '?>'), array('<?', '?>'), $text); } $text = $this->parseComments($text); $text = $this->extractNoparse($text); $text = $this->extractLoopedTags($text, $data, $callback); // Order is important here. We parse conditionals first as to avoid // unnecessary code from being parsed and executed. $text = $this->parseConditionals($text, $data, $callback); $text = $this->injectExtractions($text, 'looped_tags'); $text = $this->parseVariables($text, $data, $callback); // CMS Canvas parse variables again so we can support variables inside of variables ex: {{ {{ lang }}_title }} $text = $this->parseVariables($text, $data, $callback); $text = $this->injectExtractions($text, 'callback_blocks'); // CMS Canvas fix to extract no parse from $data $text = $this->extractNoparse($text); if ($callback) { $text = $this->parseCallbackTags($text, $data, $callback); } // To ensure that {{ noparse }} is never parsed even during consecutive parse calls // set $cumulativeNoparse to true and use self::injectNoparse($text); immediately // before the final output is sent to the browser if (!$this->cumulativeNoparse) { $text = $this->injectExtractions($text); } return $text; }
/** * Streams content parse * * Special content parser for streams. This accomplishes the following * important functions: * * a. Takes care of legacy multiple relationship parsing * b. Finds and formats special plugin fields * * @access public * @param string - the tag content * @param array - the return data * @param string - stream slug * @param string - stream namespace * @param [bool - whether or not to loop through the results or not] * @param [mixed - null or obj - stream fields. If they are availble, it will * save a mysql query.] * @param string [$id_name] The name of the id we want to pass via 'row_id'. This is almost alway 'id' * @return string - the parsed data */ public function parse_tag_content($content, $data, $stream_slug, $stream_namespace, $loop = false, $fields = null, $id_name = 'id') { // ------------------------------------- // Legacy multiple relationship provision // ------------------------------------- // We should respect our elders. This makes // sure older instances of multiple // relationships won't break. We can probably // remove this after PyroCMS 2.2 // ------------------------------------- $rep = array('{{ streams_core:related', '{{streams_core:related'); $content = str_replace($rep, '{{ streams:related stream="' . $stream_slug . '" base_namespace="' . $stream_namespace . '" entry=id ', $content); $rep = array('{{ streams_core:multiple', '{{streams_core:multiple'); $content = str_replace($rep, '{{ streams_core:multiple stream="' . $stream_slug . '" base_namespace="' . $stream_namespace . '" entry=id ', $content); // ------------------------------------- // Make sure we have our stream fields // ------------------------------------- if (is_null($fields)) { $stream = $this->stream_obj($stream_slug, $stream_namespace); $fields = $this->CI->streams_m->get_stream_fields($stream->id); } // ------------------------------------- // Custom Call Provision // ------------------------------------- // Go through the fields for this stream, and // see if any of them have the magic 'plugin_override' // function. This will allow us to call functions // from within the field type itself. // ------------------------------------- if ($fields) { foreach ($fields as $field) { if (method_exists($this->CI->type->types->{$field->field_type}, 'plugin_override')) { $content = preg_replace('/\\{\\{\\s?' . $field->field_slug . '\\s?/', '{{ streams_core:field row_id="{{ ' . $id_name . ' }}" stream_slug="' . $stream_slug . '" field_slug="' . $field->field_slug . '" namespace="' . $stream_namespace . '" field_type="' . $field->field_type . '" ', $content); $content = preg_replace('/\\{\\{\\s?\\/' . $field->field_slug . '\\s?\\}\\}/', '{{ /streams_core:field }}', $content); } } } // ------------------------------------- // Parse // ------------------------------------- $parser = new Lex_Parser(); $parser->scope_glue(':'); $parser->cumulative_noparse(true); if (!$loop) { return $parser->parse($content, $data, array($this->CI->parser, 'parser_callback')); } $out = ''; foreach ($data as $item) { $out .= $parser->parse($content, $item, array($this->CI->parser, 'parser_callback')); } return $out; }
/** * Build the entire HTML output combining partials, layouts and views. * * @access public * @param string $view * @param array $data * @param bool $return * @param bool $IE_cache * @return string */ public function build($view, $data = array(), $return = FALSE, $IE_cache = TRUE) { // Set whatever values are given. These will be available to all view files is_array($data) or $data = (array) $data; // Merge in what we already have with the specific data $this->_data = array_merge($this->_data, $data); // We don't need you any more buddy unset($data); if (empty($this->_title)) { $this->_title = $this->_guess_title(); } // Output template variables to the template $template['title'] = $this->_title; $template['breadcrumbs'] = $this->_breadcrumbs; $template['metadata'] = $this->get_metadata(); $template['partials'] = array(); // Assign by reference, as all loaded views will need access to partials $this->_data['template'] =& $template; foreach ($this->_partials as $name => $partial) { // We can only work with data arrays is_array($partial['data']) or $partial['data'] = (array) $partial['data']; // If it uses a view, load it if (isset($partial['view'])) { $template['partials'][$name] = $this->_find_view($partial['view'], $partial['data']); } else { if ($this->_parser_enabled === TRUE) { $partial['string'] = $this->_ci->parser->parse_string($partial['string'], $this->_data + $partial['data'], TRUE, TRUE); } $template['partials'][$name] = $partial['string']; } } // Disable sodding IE7's constant cacheing!! // This is in a conditional because otherwise it errors when output is returned instead of output to browser. if ($IE_cache) { $this->_ci->output->set_header('Expires: Sat, 01 Jan 2000 00:00:01 GMT'); $this->_ci->output->set_header('Cache-Control: no-store, no-cache, must-revalidate'); $this->_ci->output->set_header('Cache-Control: post-check=0, pre-check=0, max-age=0'); $this->_ci->output->set_header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); $this->_ci->output->set_header('Pragma: no-cache'); } // Let CI do the caching instead of the browser $this->cache_lifetime > 0 && $this->_ci->output->cache($this->cache_lifetime); // Test to see if this file $this->_body = $this->_find_view($view, array(), $this->_parser_body_enabled); // Want this file wrapped with a layout file? if ($this->_layout) { // Added to $this->_data['template'] by refference $template['body'] = $this->_body; if ($this->_parser_enabled) { // Persistent tags is an experiment to parse some tags after // parsing of all other tags, so the tag persistent should be: // // a) Defined only if depends of others tags // b) Plugin that is a callback, so could retrieve runtime data. // c) Returned with a content parsed $this->_data['_tags']['persistent_tags'][] = 'template:metadata'; } // Find the main body and 3rd param means parse if its a theme view (only if parser is enabled) $this->_body = self::_load_view('layouts/' . $this->_layout, $this->_data, TRUE, self::_find_view_folder()); } if ($this->_minify_enabled && function_exists('process_data_jmr1')) { $this->_body = process_data_jmr1($this->_body); } // Now that *all* parsing is sure to be done we inject the {{ noparse }} contents back into the output if (class_exists('Lex_Parser')) { $this->_body = Lex_Parser::inject_noparse($this->_body); } // Want it returned or output to browser? if (!$return) { $this->_ci->output->set_output($this->_body); } return $this->_body; }
/** * Callback from template parser * * @param array * @return mixed */ public function parser_callback($plugin, $attributes, $content) { $this->_ci->load->library('plugins'); $return_data = $this->_ci->plugins->locate($plugin, $attributes, $content); if (is_array($return_data) && $return_data) { if (!$this->_is_multi($return_data)) { $return_data = $this->_make_multi($return_data); } // $content = $data['content']; # TODO What was this doing other than throw warnings in 2.0? $parsed_return = ''; $parser = new Lex_Parser(); $parser->scope_glue(':'); foreach ($return_data as $result) { // if ($data['skip_content']) // { // $simpletags->set_skip_content($data['skip_content']); // } $parsed_return .= $parser->parse($content, $result, array($this, 'parser_callback')); } unset($parser); $return_data = $parsed_return; } return $return_data ? $return_data : null; }
/** * Parser Callback * * @param string $module * @param string $attribute * @param string $content * * @return mixed */ public function parser_callback($module, $attribute, $content) { $return_view = NULL; $parsed_return = ''; $output = self::get_view($module, $attribute); $return_view = $output; //loop it up, if its array no use in the template, gotta work it here. if (is_array($output)) { // Need to make sure we have a array and no objects inside the array too. $parser = new Lex_Parser(); $parser->scopeGlue(':'); foreach ($output as $result) { $parsed_return .= $parser->parse($content, $result, array($this, 'parser_callback')); } unset($parser); $return_view = $parsed_return; } return $return_view; }
/** * Streams content parse * * Special content parser for PyroStreams plugin * * @access private * @param string - the tag content * @param array - the return data * @param string - stream slug * @param [bool - whether or not to loop through the results or not] * @return string - the parsed data */ private function streams_content_parse($content, $data, $stream_slug, $loop = false) { // ------------------------------------- // Multiple Provision // ------------------------------------- // Automatically add in multiple streams data. // This makes it easier to call the multiple function // from within the streams tags // ------------------------------------- $rep = array('{{ streams:related', '{{streams:related'); $content = str_replace($rep, '{{ streams:related stream="' . $stream_slug . '" entry=id ', $content); $rep = array('{{ streams:multiple', '{{streams:multiple'); $content = str_replace($rep, '{{ streams:multiple stream="' . $stream_slug . '" entry=id ', $content); // ------------------------------------- // Parse // ------------------------------------- $parser = new Lex_Parser(); $parser->scope_glue(':'); if (!$loop) { return $parser->parse($content, $data, array($this->parser, 'parser_callback')); } $out = ''; foreach ($data as $item) { $out .= $parser->parse($content, $item, array($this->parser, 'parser_callback')); } return $out; }
/** * Build the entire HTML output combining partials, layouts and views. * * @param string $view * @param array $data * @param bool $return * @param bool $IE_cache * @param bool $pre_parsed_view Did we already parse our view? * @return string */ public function build($view, $data = array(), $return = false, $IE_cache = true, $pre_parsed_view = false, $template = array()) { // Set whatever values are given. These will be available to all view files is_array($data) or $data = (array) $data; // Merge in what we already have set $this->_data = array_merge($this->_data, $data); // We don't need you any more buddy unset($data); // If you want, you can use the build_template_data() // to pre-build this template data. This is an edge case so you'll // probably always just leave it to array(), but it's here if // you need it. if (!$template) { $template = $this->build_template_data(); } $this->_data['template'] =& $template; // Process partials. foreach ($this->_partials as $name => $partial) { // We can only work with data arrays is_array($partial['data']) or $partial['data'] = (array) $partial['data']; // If it uses a view, load it if (isset($partial['view'])) { $template['partials'][$name] = $this->_find_view($partial['view'], $partial['data']); } else { if ($this->_parser_enabled === true) { $partial['string'] = $this->_ci->parser->parse_string($partial['string'], $this->_data + $partial['data'], true, true); } $template['partials'][$name] = $partial['string']; } } // Disable sodding IE7's constant cacheing!! // This is in a conditional because otherwise it errors when output is returned instead of output to browser. if ($IE_cache) { $this->_ci->output->set_header('Expires: Sat, 01 Jan 2000 00:00:01 GMT'); $this->_ci->output->set_header('Cache-Control: no-store, no-cache, must-revalidate'); $this->_ci->output->set_header('Cache-Control: post-check=0, pre-check=0, max-age=0'); $this->_ci->output->set_header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); $this->_ci->output->set_header('Pragma: no-cache'); } // Let CI do the caching instead of the browser $this->cache_lifetime > 0 && $this->_ci->output->cache($this->cache_lifetime); // Set the _body var. If we have pre-parsed our // view, then our work is done. Otherwise, we will // find the view and parse it. if ($pre_parsed_view) { $this->_body = $view; } else { $this->_body = $this->_find_view($view, array(), $this->_parser_body_enabled); } // Want this file wrapped with a layout file? if ($this->_layout) { // Added to $this->_data['template'] by refference $template['body'] = $this->_body; if ($this->_parser_enabled) { // Persistent tags is an experiment to parse some tags after // parsing of all other tags, so the tag persistent should be: // // a) Defined only if depends of others tags // b) Plugin that is a callback, so could retrieve runtime data. // c) Returned with a content parsed $this->_data['_tags']['persistent_tags'][] = 'template:metadata'; } // Find the main body and 3rd param means parse if its a theme view (only if parser is enabled) $this->_body = self::_load_view('layouts/' . $this->_layout, $this->_data, true, self::_find_view_folder()); } if ($this->_minify_enabled && function_exists('process_data_jmr1')) { $this->_body = process_data_jmr1($this->_body); } // Now that *all* parsing is sure to be done we inject the {{ noparse }} contents back into the output if (class_exists('Lex_Parser')) { $this->_body = Lex_Parser::inject_noparse($this->_body); } // Want it returned or output to browser? if (!$return) { $this->_ci->output->set_output($this->_body); } $this->_stream = array(); return $this->_body; }
/** * Parse chunk content * * @access private * @param string - the chunk content * @param string - parse Lex tags? - yes/no * @return string */ private function parse_chunk($content, $parse_tags) { // Lex tags are parsed by default. If you want to // turn off parsing Lex tags, just set parse_tags to 'no' if ($parse_tags == 'yes') { $parser = new Lex_Parser(); $parser->scope_glue(':'); return $parser->parse($content, array(), array($this->parser, 'parser_callback')); } else { return $content; } }
/** * Main Fizl Function * * Routes and processes all page requests * * @access public * @return void */ public function _remap() { $this->load->library('Plugin'); $this->load->library('Parse'); $this->load->config('fizl'); include APPPATH . 'libraries/Lex/Autoloader.php'; Lex_Autoloader::register(); $this->load->helper(array('file', 'url')); // ------------------------------------- // Site config load // ------------------------------------- $raw_configs = (require_once FCPATH . 'config.php'); foreach ($config as $key => $var) { $this->config->set_item($key, $var); } // ------------------------------------- // Configs // ------------------------------------- // We do this first since we need this // data // ------------------------------------- $this->vars = array('segment_1' => $this->uri->segment(1), 'segment_2' => $this->uri->segment(2), 'segment_3' => $this->uri->segment(3), 'segment_4' => $this->uri->segment(4), 'segment_5' => $this->uri->segment(5), 'segment_6' => $this->uri->segment(6), 'segment_7' => $this->uri->segment(7), 'current_year' => date('Y'), 'current_url' => current_url(), 'site_url' => site_url(), 'base_url' => $this->config->item('base_url')); // Set the site folder as a constant define('SITE_FOLDER', $this->config->item('site_folder')); // ------------------------------------- // Look for page // ------------------------------------- // So... does this file exist? $segments = $this->uri->segment_array(); $is_home = FALSE; // Blank mean it's the home page, ya hurd? if (empty($segments)) { $is_home = TRUE; $segments = array('index'); } // ------------------------------------- // Find filename // ------------------------------------- // Is this a folder? If so we are looking for the index // file in the folder. if (is_dir(SITE_FOLDER . '/' . implode('/', $segments))) { $file = 'index'; } else { // Okay let's take a look at the last element $file = array_pop($segments); } // Turn the URL into a file path $file_path = SITE_FOLDER; if ($segments) { $file_path .= '/' . implode('/', $segments); } // ------------------------------------- // Find file // ------------------------------------- // We just want two things $file_elems = array_slice(explode('.', $file), 0, 2); $supported_files = array('html', 'md', 'textile'); $file_ext = NULL; // If there is a file extenison, // we just add it here. if (count($file_elems) == 2) { $file_ext = $file_elems[1]; $file_path .= '/' . $file; } else { // Try and find a file to match // our URL foreach ($supported_files as $ext) { if (file_exists($file_path . '/' . $file . '.' . $ext)) { $file_ext = $ext; $file_path .= '/' . $file . '.' . $ext; break; } } } // ------------------------------------- // Set headers // ------------------------------------- if (!$file_ext) { // No file for this? Set us a 404 header('HTTP/1.0 404 Not Found'); $is_404 = true; } else { $is_404 = false; $this->output->set_content_type('text/html'); } // ------------------------------------- // Set Template // ------------------------------------- $template = FALSE; $template_path = FCPATH . $this->config->item('assets_folder') . '/templates/'; if ($is_home and is_file($template_path . 'home.html')) { $template = read_file($template_path . 'home.html'); } elseif ($is_404) { $template = read_file($template_path . '404.html'); // Do we have a template for this folder? } elseif (is_file($template_path . implode('_', $segments) . '.html')) { $template = read_file($template_path . implode('_', $segments) . '.html'); } elseif (is_file($template_path . 'sub.html')) { $template = read_file($template_path . 'sub.html'); } elseif (is_file($template_path . 'default.html')) { $template = read_file($template_path . 'default.html'); } // ------------------------------------- // Get Content // ------------------------------------- if (!$is_404) { $content = read_file($file_path); // ------------------------------------- // Prep content by filetype // ------------------------------------- // .md and .textile get formatted // automatically. // ------------------------------------- if ($file_ext == 'md') { $content = '{{ format }}' . $content . '{{ /format }}'; } elseif ($file_ext == 'textile') { $content = '{{ format method="textile" }}' . $content . '{{ /format }}'; } // ------------------------------------- // If we have no template, then // we just use the content. if (!$template) { $template = $content; } else { // If we have a template, let's be // sneakty and add in the content // variable manually. $template = str_replace(array('{{ content }}', '{{content}}'), $content, $template); } // Our content is avialble $this->vars['content'] = $content; } // ------------------------------------- // Prep and Output Content // ------------------------------------- $parser = new Lex_Parser(); $parser->scope_glue(':'); echo $parser->parse($template, $this->vars, array($this->parse, 'callback'), true); }
/** * Streams content parse * * Special content parser for PyroStreams plugin * * @access private * @param string - the tag content * @param array - the return data * @param string - stream slug * @return string - the parsed data */ private function streams_content_parse($content, $data, $stream_slug) { // ------------------------------------- // Multiple Provision // ------------------------------------- // Automatically add in multiple streams data. // This makes it easier to call the multiple function // from within the streams tags // ------------------------------------- $rep = array('{{ streams:related', '{{streams:related'); $content = str_replace($rep, '{{ streams:related stream="' . $stream_slug . '" entry="{{ id }}" ', $content); $rep = array('{{ streams:multiple', '{{streams:multiple'); $content = str_replace($rep, '{{ streams:multiple stream="' . $stream_slug . '" entry="{{ id }}" ', $content); // ------------------------------------- // Parse Rows // ------------------------------------- $parser = new Lex_Parser(); $parser->scope_glue(':'); return $parser->parse($content, $data, array($this->parser, 'parser_callback')); }
/** * Callback from template parser * * @param array * @return mixed */ public function parser_callback($plugin, $attributes, $content, $data) { $this->_ci->load->library('plugins'); $return_data = ''; // Check if there were local data callbacks defined if (isset($data['_callbacks'][$plugin])) { $callback = $data['_callbacks'][$plugin]; if (is_callable($callback)) { $return_data = call_user_func_array($callback, array($plugin, $attributes, $content, $data)); } } else { if (isset($this->callbacks[$plugin])) { $callback = $this->callbacks[$plugin]; if (is_callable($callback)) { $return_data = call_user_func_array($callback, array($plugin, $attributes, $content, $data)); } } else { // Locate and process plugin $return_data = $this->_ci->plugins->locate($plugin, $attributes, $content, $data); } } if (is_array($return_data)) { if (!$this->_is_multi($return_data)) { $return_data = $this->_make_multi($return_data); } // Check if plugin has made any changes to the default content if (isset($return_data['_content'])) { $content = $return_data['_content']; unset($return_data['_content']); } $parsed_return = ''; $parser = new Lex_Parser(); $parser->scopeGlue(':'); foreach ($return_data as $result) { // Check if there was content declared for the result // If no _content declared in result array use default content if (isset($result['_content'])) { $rendered_content = $result['_content']; unset($result['_content']); } else { $rendered_content = $content; } $parsed_return .= $parser->parse($rendered_content, $result, array($this, 'parser_callback')); } unset($parser); $return_data = $parsed_return; } return $return_data ? $return_data : NULL; }
/** * Parse Template Embeds * * @access public * @param string - file to embed * @param array - new vars * @return string */ private function embed($file, $attributes) { // Load the file. Always an .html $embed_content = read_file(FCPATH . $this->vars['assets_folder'] . '/embeds/' . $file . '.html'); if (!$embed_content) { return NULL; } if (!empty($attributes)) { return $embed_content; } $parser = new Lex_Parser(); $parser->scope_glue(':'); $parser->parse_variables($embed_content, $attributes); }
/** * Parse Content * * @access public * @param array * @return string */ public function callback($name, $attributes, $content) { $this->CI = get_instance(); // ---------------------------- // Determine Call // ---------------------------- // Do we have a : in the name? If so, we need // to separate this into the plugin/call if (strpos($name, ':') === FALSE) { // If we do not have a call // specified, we can use a function // with the same name as the plugin. $plugin = $name; $call = $name; } else { $pieces = explode(':', $name, 2); if (count($pieces) != 2) { return NULL; } $plugin = $pieces[0]; $call = $pieces[1]; } // Easy out for configs. // We have a special place in our heart for config:config_item. if ($plugin == 'config') { return $this->CI->config->item($call); } // ---------------------------- // Find & Load Plugin Class // ---------------------------- $plugin_dirs = array(APPPATH . 'plugins/', FCPATH . 'addons/plugins/'); // We can either have plugin folders or plugin files. foreach ($plugin_dirs as $dir) { if (is_dir($dir . $plugin) and is_file($dir . $plugin . '/' . $plugin . '.php')) { $file = $dir . $plugin . '/' . $plugin . '.php'; break; } elseif (is_file($dir . $plugin . '.php')) { $file = $dir . $plugin . '.php'; break; } } if (!isset($file)) { return NULL; } require_once $file; $class = 'Plugin_' . $plugin; if (class_exists($class)) { $plug = new $class(); } // ---------------------------- // Attributes // ---------------------------- // Add our params to the library // as class variables // ---------------------------- foreach ($attributes as $key => $val) { $plug->attributes[$key] = $val; } // Add content to the library $plug->tag_content = $content; if (!method_exists($plug, $call)) { return NULL; } // ---------------------------- // Make Plugin Call // ---------------------------- $return = $plug->{$call}(); // ---------------------------- // Return data based on type // ---------------------------- if (is_array($return)) { $parser = new Lex_Parser(); $parser->scope_glue(':'); return $parser->parse($content, $return, array($this->CI->parse, 'callback')); } elseif (is_string($return)) { return $return; } else { return NULL; } }