Complex classes like SSViewer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use SSViewer, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 680 | class SSViewer implements Flushable { |
||
| 681 | |||
| 682 | /** |
||
| 683 | * @config |
||
| 684 | * @var boolean $source_file_comments |
||
| 685 | */ |
||
| 686 | private static $source_file_comments = false; |
||
| 687 | |||
| 688 | /** |
||
| 689 | * @ignore |
||
| 690 | */ |
||
| 691 | private static $template_cache_flushed = false; |
||
| 692 | |||
| 693 | /** |
||
| 694 | * @ignore |
||
| 695 | */ |
||
| 696 | private static $cacheblock_cache_flushed = false; |
||
| 697 | |||
| 698 | /** |
||
| 699 | * Set whether HTML comments indicating the source .SS file used to render this page should be |
||
| 700 | * included in the output. This is enabled by default |
||
| 701 | * |
||
| 702 | * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead |
||
| 703 | * @param boolean $val |
||
| 704 | */ |
||
| 705 | public static function set_source_file_comments($val) { |
||
| 706 | Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead'); |
||
| 707 | Config::inst()->update('SSViewer', 'source_file_comments', $val); |
||
| 708 | } |
||
| 709 | |||
| 710 | /** |
||
| 711 | * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead |
||
| 712 | * @return boolean |
||
| 713 | */ |
||
| 714 | public static function get_source_file_comments() { |
||
| 715 | Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead'); |
||
| 716 | return Config::inst()->get('SSViewer', 'source_file_comments'); |
||
| 717 | } |
||
| 718 | |||
| 719 | /** |
||
| 720 | * @var array $chosenTemplates Associative array for the different |
||
| 721 | * template containers: "main" and "Layout". Values are absolute file paths to *.ss files. |
||
| 722 | */ |
||
| 723 | private $chosenTemplates = array(); |
||
| 724 | |||
| 725 | /** |
||
| 726 | * @var boolean |
||
| 727 | */ |
||
| 728 | protected $rewriteHashlinks = true; |
||
| 729 | |||
| 730 | /** |
||
| 731 | * @config |
||
| 732 | * @var string The used "theme", which usually consists of templates, images and stylesheets. |
||
| 733 | * Only used when {@link $theme_enabled} is set to TRUE. |
||
| 734 | */ |
||
| 735 | private static $theme = null; |
||
| 736 | |||
| 737 | /** |
||
| 738 | * @config |
||
| 739 | * @var boolean Use the theme. Set to FALSE in order to disable themes, |
||
| 740 | * which can be useful for scenarios where theme overrides are temporarily undesired, |
||
| 741 | * such as an administrative interface separate from the website theme. |
||
| 742 | * It retains the theme settings to be re-enabled, for example when a website content |
||
| 743 | * needs to be rendered from within this administrative interface. |
||
| 744 | */ |
||
| 745 | private static $theme_enabled = true; |
||
| 746 | |||
| 747 | /** |
||
| 748 | * @var boolean |
||
| 749 | */ |
||
| 750 | protected $includeRequirements = true; |
||
| 751 | |||
| 752 | /** |
||
| 753 | * @var TemplateParser |
||
| 754 | */ |
||
| 755 | protected $parser; |
||
| 756 | |||
| 757 | /* |
||
| 758 | * Default prepended cache key for partial caching |
||
| 759 | * |
||
| 760 | * @var string |
||
| 761 | * @config |
||
| 762 | */ |
||
| 763 | private static $global_key = '$CurrentReadingMode, $CurrentUser.ID'; |
||
| 764 | |||
| 765 | /** |
||
| 766 | * Triggered early in the request when someone requests a flush. |
||
| 767 | */ |
||
| 768 | public static function flush() { |
||
| 769 | self::flush_template_cache(true); |
||
| 770 | self::flush_cacheblock_cache(true); |
||
| 771 | } |
||
| 772 | |||
| 773 | /** |
||
| 774 | * Create a template from a string instead of a .ss file |
||
| 775 | * |
||
| 776 | * @param string $content The template content |
||
| 777 | * @param bool|void $cacheTemplate Whether or not to cache the template from string |
||
| 778 | * @return SSViewer |
||
| 779 | */ |
||
| 780 | public static function fromString($content, $cacheTemplate = null) { |
||
| 781 | $viewer = new SSViewer_FromString($content); |
||
| 782 | if ($cacheTemplate !== null) { |
||
| 783 | $viewer->setCacheTemplate($cacheTemplate); |
||
| 784 | } |
||
| 785 | return $viewer; |
||
| 786 | } |
||
| 787 | |||
| 788 | /** |
||
| 789 | * @deprecated 4.0 Use the "SSViewer.theme" config setting instead |
||
| 790 | * @param string $theme The "base theme" name (without underscores). |
||
| 791 | */ |
||
| 792 | public static function set_theme($theme) { |
||
| 793 | Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead'); |
||
| 794 | Config::inst()->update('SSViewer', 'theme', $theme); |
||
| 795 | } |
||
| 796 | |||
| 797 | /** |
||
| 798 | * @deprecated 4.0 Use the "SSViewer.theme" config setting instead |
||
| 799 | * @return string |
||
| 800 | */ |
||
| 801 | public static function current_theme() { |
||
| 802 | Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead'); |
||
| 803 | return Config::inst()->get('SSViewer', 'theme'); |
||
| 804 | } |
||
| 805 | |||
| 806 | /** |
||
| 807 | * Returns the path to the theme folder |
||
| 808 | * |
||
| 809 | * @return string |
||
| 810 | */ |
||
| 811 | public static function get_theme_folder() { |
||
| 812 | $theme = Config::inst()->get('SSViewer', 'theme'); |
||
| 813 | return $theme ? THEMES_DIR . "/" . $theme : project(); |
||
| 814 | } |
||
| 815 | |||
| 816 | /** |
||
| 817 | * Returns an array of theme names present in a directory. |
||
| 818 | * |
||
| 819 | * @param string $path |
||
| 820 | * @param bool $subthemes Include subthemes (default false). |
||
| 821 | * @return array |
||
| 822 | */ |
||
| 823 | public static function get_themes($path = null, $subthemes = false) { |
||
| 824 | $path = rtrim($path ? $path : THEMES_PATH, '/'); |
||
| 825 | $themes = array(); |
||
| 826 | |||
| 827 | if (!is_dir($path)) return $themes; |
||
| 828 | |||
| 829 | foreach (scandir($path) as $item) { |
||
| 830 | if ($item[0] != '.' && is_dir("$path/$item")) { |
||
| 831 | if ($subthemes || strpos($item, '_') === false) { |
||
| 832 | $themes[$item] = $item; |
||
| 833 | } |
||
| 834 | } |
||
| 835 | } |
||
| 836 | |||
| 837 | return $themes; |
||
| 838 | } |
||
| 839 | |||
| 840 | /** |
||
| 841 | * @deprecated since version 4.0 |
||
| 842 | * @return string |
||
| 843 | */ |
||
| 844 | public static function current_custom_theme(){ |
||
| 845 | Deprecation::notice('4.0', 'Use the "SSViewer.theme" and "SSViewer.theme_enabled" config settings instead'); |
||
| 846 | return Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null; |
||
| 847 | } |
||
| 848 | |||
| 849 | /** |
||
| 850 | * Traverses the given the given class context looking for templates with the relevant name. |
||
| 851 | * |
||
| 852 | * @param $className string - valid class name |
||
| 853 | * @param $suffix string |
||
| 854 | * @param $baseClass string |
||
| 855 | * |
||
| 856 | * @return array |
||
| 857 | */ |
||
| 858 | public static function get_templates_by_class($className, $suffix = '', $baseClass = null) { |
||
| 859 | // Figure out the class name from the supplied context. |
||
| 860 | if(!is_string($className) || !class_exists($className)) { |
||
| 861 | throw new InvalidArgumentException('SSViewer::get_templates_by_class() expects a valid class name as ' . |
||
| 862 | 'its first parameter.'); |
||
| 863 | return array(); |
||
| 864 | } |
||
| 865 | $templates = array(); |
||
| 866 | $classes = array_reverse(ClassInfo::ancestry($className)); |
||
| 867 | foreach($classes as $class) { |
||
| 868 | $template = $class . $suffix; |
||
| 869 | if(SSViewer::hasTemplate($template)) $templates[] = $template; |
||
| 870 | |||
| 871 | // If the class is "Page_Controller", look for Page.ss |
||
| 872 | if(stripos($class,'_controller') !== false) { |
||
| 873 | $template = str_ireplace('_controller','',$class) . $suffix; |
||
| 874 | if(SSViewer::hasTemplate($template)) $templates[] = $template; |
||
| 875 | } |
||
| 876 | |||
| 877 | if($baseClass && $class == $baseClass) break; |
||
| 878 | } |
||
| 879 | return $templates; |
||
| 880 | } |
||
| 881 | |||
| 882 | /** |
||
| 883 | * @param string|array $templateList If passed as a string with .ss extension, used as the "main" template. |
||
| 884 | * If passed as an array, it can be used for template inheritance (first found template "wins"). |
||
| 885 | * Usually the array values are PHP class names, which directly correlate to template names. |
||
| 886 | * <code> |
||
| 887 | * array('MySpecificPage', 'MyPage', 'Page') |
||
| 888 | * </code> |
||
| 889 | */ |
||
| 890 | public function __construct($templateList, TemplateParser $parser = null) { |
||
| 891 | if ($parser) { |
||
| 892 | $this->setParser($parser); |
||
| 893 | } |
||
| 894 | |||
| 895 | if(!is_array($templateList) && substr((string) $templateList,-3) == '.ss') { |
||
| 896 | $this->chosenTemplates['main'] = $templateList; |
||
| 897 | } else { |
||
| 898 | if(Config::inst()->get('SSViewer', 'theme_enabled')) { |
||
| 899 | $theme = Config::inst()->get('SSViewer', 'theme'); |
||
| 900 | } else { |
||
| 901 | $theme = null; |
||
| 902 | } |
||
| 903 | $this->chosenTemplates = SS_TemplateLoader::instance()->findTemplates( |
||
| 904 | $templateList, $theme |
||
| 905 | ); |
||
| 906 | } |
||
| 907 | |||
| 908 | if(!$this->chosenTemplates) { |
||
| 909 | $templateList = (is_array($templateList)) ? $templateList : array($templateList); |
||
| 910 | |||
| 911 | $message = 'None of the following templates could be found'; |
||
| 912 | if(!$theme) { |
||
| 913 | $message .= ' (no theme in use)'; |
||
| 914 | } else { |
||
| 915 | $message .= ' in theme "' . $theme . '"'; |
||
| 916 | } |
||
| 917 | |||
| 918 | user_error($message . ': ' . implode(".ss, ", $templateList) . ".ss", E_USER_WARNING); |
||
| 919 | } |
||
| 920 | } |
||
| 921 | |||
| 922 | /** |
||
| 923 | * Set the template parser that will be used in template generation |
||
| 924 | * @param \TemplateParser $parser |
||
| 925 | */ |
||
| 926 | public function setParser(TemplateParser $parser) |
||
| 927 | { |
||
| 928 | $this->parser = $parser; |
||
| 929 | } |
||
| 930 | |||
| 931 | /** |
||
| 932 | * Returns the parser that is set for template generation |
||
| 933 | * @return \TemplateParser |
||
| 934 | */ |
||
| 935 | public function getParser() |
||
| 936 | { |
||
| 937 | if (!$this->parser) { |
||
| 938 | $this->setParser(Injector::inst()->get('SSTemplateParser')); |
||
| 939 | } |
||
| 940 | return $this->parser; |
||
| 941 | } |
||
| 942 | |||
| 943 | /** |
||
| 944 | * Returns true if at least one of the listed templates exists. |
||
| 945 | * |
||
| 946 | * @param array $templates |
||
| 947 | * |
||
| 948 | * @return boolean |
||
| 949 | */ |
||
| 950 | public static function hasTemplate($templates) { |
||
| 951 | $manifest = SS_TemplateLoader::instance()->getManifest(); |
||
| 952 | |||
| 953 | if(Config::inst()->get('SSViewer', 'theme_enabled')) { |
||
| 954 | $theme = Config::inst()->get('SSViewer', 'theme'); |
||
| 955 | } else { |
||
| 956 | $theme = null; |
||
| 957 | } |
||
| 958 | |||
| 959 | foreach ((array) $templates as $template) { |
||
| 960 | if ($manifest->getCandidateTemplate($template, $theme)) return true; |
||
| 961 | } |
||
| 962 | |||
| 963 | return false; |
||
| 964 | } |
||
| 965 | |||
| 966 | /** |
||
| 967 | * Set a global rendering option. |
||
| 968 | * |
||
| 969 | * The following options are available: |
||
| 970 | * - rewriteHashlinks: If true (the default), <a href="#..."> will be rewritten to contain the |
||
| 971 | * current URL. This lets it play nicely with our <base> tag. |
||
| 972 | * - If rewriteHashlinks = 'php' then, a piece of PHP script will be inserted before the hash |
||
| 973 | * links: "<?php echo $_SERVER['REQUEST_URI']; ?>". This is useful if you're generating a |
||
| 974 | * page that will be saved to a .php file and may be accessed from different URLs. |
||
| 975 | * |
||
| 976 | * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead |
||
| 977 | * @param string $optionName |
||
| 978 | * @param mixed $optionVal |
||
| 979 | */ |
||
| 980 | public static function setOption($optionName, $optionVal) { |
||
| 981 | if($optionName == 'rewriteHashlinks') { |
||
| 982 | Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead'); |
||
| 983 | Config::inst()->update('SSViewer', 'rewrite_hash_links', $optionVal); |
||
| 984 | } else { |
||
| 985 | Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead'); |
||
| 986 | Config::inst()->update('SSViewer', $optionName, $optionVal); |
||
| 987 | } |
||
| 988 | } |
||
| 989 | |||
| 990 | /** |
||
| 991 | * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead |
||
| 992 | * @param string |
||
| 993 | * @return mixed |
||
| 994 | */ |
||
| 995 | public static function getOption($optionName) { |
||
| 996 | if($optionName == 'rewriteHashlinks') { |
||
| 997 | Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead'); |
||
| 998 | return Config::inst()->get('SSViewer', 'rewrite_hash_links'); |
||
| 999 | } else { |
||
| 1000 | Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead'); |
||
| 1001 | return Config::inst()->get('SSViewer', $optionName); |
||
| 1002 | } |
||
| 1003 | } |
||
| 1004 | |||
| 1005 | /** |
||
| 1006 | * @config |
||
| 1007 | * @var boolean |
||
| 1008 | */ |
||
| 1009 | private static $rewrite_hash_links = true; |
||
| 1010 | |||
| 1011 | protected static $topLevel = array(); |
||
| 1012 | |||
| 1013 | public static function topLevel() { |
||
| 1014 | if(SSViewer::$topLevel) { |
||
| 1015 | return SSViewer::$topLevel[sizeof(SSViewer::$topLevel)-1]; |
||
| 1016 | } |
||
| 1017 | } |
||
| 1018 | |||
| 1019 | /** |
||
| 1020 | * @return Zend_Cache_Core |
||
| 1021 | */ |
||
| 1022 | protected static function defaultPartialCacheStore() |
||
| 1023 | { |
||
| 1024 | return SS_Cache::factory('cacheblock', 'Output', array('disable-segmentation' => true)); |
||
| 1025 | } |
||
| 1026 | |||
| 1027 | /** |
||
| 1028 | * Call this to disable rewriting of <a href="#xxx"> links. This is useful in Ajax applications. |
||
| 1029 | * It returns the SSViewer objects, so that you can call new SSViewer("X")->dontRewriteHashlinks()->process(); |
||
| 1030 | */ |
||
| 1031 | public function dontRewriteHashlinks() { |
||
| 1032 | $this->rewriteHashlinks = false; |
||
| 1033 | Config::inst()->update('SSViewer', 'rewrite_hash_links', false); |
||
| 1034 | return $this; |
||
| 1035 | } |
||
| 1036 | |||
| 1037 | public function exists() { |
||
| 1038 | return $this->chosenTemplates; |
||
| 1039 | } |
||
| 1040 | |||
| 1041 | /** |
||
| 1042 | * @param string $identifier A template name without '.ss' extension or path |
||
| 1043 | * @param string $type The template type, either "main", "Includes" or "Layout" |
||
| 1044 | * |
||
| 1045 | * @return string Full system path to a template file |
||
| 1046 | */ |
||
| 1047 | public static function getTemplateFileByType($identifier, $type) { |
||
| 1048 | $loader = SS_TemplateLoader::instance(); |
||
| 1049 | if(Config::inst()->get('SSViewer', 'theme_enabled')) { |
||
| 1050 | $theme = Config::inst()->get('SSViewer', 'theme'); |
||
| 1051 | } else { |
||
| 1052 | $theme = null; |
||
| 1053 | } |
||
| 1054 | $found = $loader->findTemplates("$type/$identifier", $theme); |
||
| 1055 | |||
| 1056 | if (isset($found['main'])) { |
||
| 1057 | return $found['main']; |
||
| 1058 | } |
||
| 1059 | else if (!empty($found)) { |
||
| 1060 | $founds = array_values($found); |
||
| 1061 | return $founds[0]; |
||
| 1062 | } |
||
| 1063 | } |
||
| 1064 | |||
| 1065 | /** |
||
| 1066 | * Clears all parsed template files in the cache folder. |
||
| 1067 | * |
||
| 1068 | * Can only be called once per request (there may be multiple SSViewer instances). |
||
| 1069 | * |
||
| 1070 | * @param bool $force Set this to true to force a re-flush. If left to false, flushing |
||
| 1071 | * may only be performed once a request. |
||
| 1072 | */ |
||
| 1073 | public static function flush_template_cache($force = false) { |
||
| 1082 | |||
| 1083 | /** |
||
| 1084 | * Clears all partial cache blocks. |
||
| 1085 | * |
||
| 1086 | * Can only be called once per request (there may be multiple SSViewer instances). |
||
| 1087 | * |
||
| 1088 | * @param bool $force Set this to true to force a re-flush. If left to false, flushing |
||
| 1089 | * may only be performed once a request. |
||
| 1090 | */ |
||
| 1091 | public static function flush_cacheblock_cache($force = false) { |
||
| 1110 | |||
| 1111 | /** |
||
| 1112 | * @var Zend_Cache_Core |
||
| 1113 | */ |
||
| 1114 | protected $partialCacheStore = null; |
||
| 1115 | |||
| 1116 | /** |
||
| 1117 | * Set the cache object to use when storing / retrieving partial cache blocks. |
||
| 1118 | * |
||
| 1119 | * @param Zend_Cache_Core $cache |
||
| 1120 | */ |
||
| 1121 | public function setPartialCacheStore($cache) { |
||
| 1124 | |||
| 1125 | /** |
||
| 1126 | * Get the cache object to use when storing / retrieving partial cache blocks. |
||
| 1127 | * |
||
| 1128 | * @return Zend_Cache_Core |
||
| 1129 | */ |
||
| 1130 | public function getPartialCacheStore() { |
||
| 1133 | |||
| 1134 | /** |
||
| 1135 | * Flag whether to include the requirements in this response. |
||
| 1136 | * |
||
| 1137 | * @param boolean |
||
| 1138 | */ |
||
| 1139 | public function includeRequirements($incl = true) { |
||
| 1142 | |||
| 1143 | /** |
||
| 1144 | * An internal utility function to set up variables in preparation for including a compiled |
||
| 1145 | * template, then do the include |
||
| 1146 | * |
||
| 1147 | * Effectively this is the common code that both SSViewer#process and SSViewer_FromString#process call |
||
| 1148 | * |
||
| 1149 | * @param string $cacheFile - The path to the file that contains the template compiled to PHP |
||
| 1150 | * @param SS_Object $item - The item to use as the root scope for the template |
||
| 1151 | * @param array|null $overlay - Any variables to layer on top of the scope |
||
| 1152 | * @param array|null $underlay - Any variables to layer underneath the scope |
||
| 1153 | * @param SS_Object $inheritedScope - the current scope of a parent template including a sub-template |
||
| 1154 | * |
||
| 1155 | * @return string - The result of executing the template |
||
| 1156 | */ |
||
| 1157 | protected function includeGeneratedTemplate($cacheFile, $item, $overlay, $underlay, $inheritedScope = null) { |
||
| 1176 | |||
| 1177 | /** |
||
| 1178 | * The process() method handles the "meat" of the template processing. |
||
| 1179 | * |
||
| 1180 | * It takes care of caching the output (via {@link SS_Cache}), as well as |
||
| 1181 | * replacing the special "$Content" and "$Layout" placeholders with their |
||
| 1182 | * respective subtemplates. |
||
| 1183 | * |
||
| 1184 | * The method injects extra HTML in the header via {@link Requirements::includeInHTML()}. |
||
| 1185 | * |
||
| 1186 | * Note: You can call this method indirectly by {@link ViewableData->renderWith()}. |
||
| 1187 | * |
||
| 1188 | * @param ViewableData $item |
||
| 1189 | * @param array|null $arguments - arguments to an included template |
||
| 1190 | * @param SS_Object $inheritedScope - the current scope of a parent template including a sub-template |
||
| 1191 | * |
||
| 1192 | * @return HTMLText Parsed template output. |
||
| 1193 | */ |
||
| 1194 | public function process($item, $arguments = null, $inheritedScope = null) { |
||
| 1195 | SSViewer::$topLevel[] = $item; |
||
| 1196 | |||
| 1197 | if(isset($this->chosenTemplates['main'])) { |
||
| 1259 | |||
| 1260 | /** |
||
| 1261 | * Execute the given template, passing it the given data. |
||
| 1262 | * Used by the <% include %> template tag to process templates. |
||
| 1263 | * |
||
| 1264 | * @param string $template Template name |
||
| 1265 | * @param mixed $data Data context |
||
| 1266 | * @param array $arguments Additional arguments |
||
| 1267 | * @return string Evaluated result |
||
| 1268 | */ |
||
| 1269 | public static function execute_template($template, $data, $arguments = null, $scope = null) { |
||
| 1275 | |||
| 1276 | /** |
||
| 1277 | * Execute the evaluated string, passing it the given data. |
||
| 1278 | * Used by partial caching to evaluate custom cache keys expressed using |
||
| 1279 | * template expressions |
||
| 1280 | * |
||
| 1281 | * @param string $content Input string |
||
| 1282 | * @param mixed $data Data context |
||
| 1283 | * @param array $arguments Additional arguments |
||
| 1284 | * @return string Evaluated result |
||
| 1285 | */ |
||
| 1286 | public static function execute_string($content, $data, $arguments = null) { |
||
| 1292 | |||
| 1293 | public function parseTemplateContent($content, $template="") { |
||
| 1300 | |||
| 1301 | /** |
||
| 1302 | * Returns the filenames of the template that will be rendered. It is a map that may contain |
||
| 1303 | * 'Content' & 'Layout', and will have to contain 'main' |
||
| 1304 | */ |
||
| 1305 | public function templates() { |
||
| 1308 | |||
| 1309 | /** |
||
| 1310 | * @param string $type "Layout" or "main" |
||
| 1311 | * @param string $file Full system path to the template file |
||
| 1312 | */ |
||
| 1313 | public function setTemplateFile($type, $file) { |
||
| 1316 | |||
| 1317 | /** |
||
| 1318 | * Return an appropriate base tag for the given template. |
||
| 1319 | * It will be closed on an XHTML document, and unclosed on an HTML document. |
||
| 1320 | * |
||
| 1321 | * @param $contentGeneratedSoFar The content of the template generated so far; it should contain |
||
| 1322 | * the DOCTYPE declaration. |
||
| 1323 | */ |
||
| 1324 | public static function get_base_tag($contentGeneratedSoFar) { |
||
| 1334 | } |
||
| 1335 | |||
| 1410 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: