ttrss/tw/lang/TW_base.php

472 lines
12 KiB
PHP

<?php
/*
* tag|wall | PHP Tag Filter |
* ---------------------------------------------------------------------
Copyright (C) 2002 Juraj 'HVGE' Durech
Copyright (C) 2002 www.designia.sk
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* ---------------------------------------------------------------------
*/
/*
* $this->pt text
* $this->pti text index
*/
class TW_base // TW_lang extends this class
{
var $error; // error module object
var $output; // output module object
var $out; // output string
var $content_off;
var $tags; // relationships between tags
var $config; // current configuration (twParser::strip_tags())
var $config_tags; // tags in current configuration
var $config_attr; // attributes in current configuration
var $config_req_attr; // required attributes
var $TAG;
var $is_attributes;
var $ATTRIBUTES; // index => array("atr","value")
var $ATTR; // array(atr,value)
var $VALUE;
/********************************************************************************************
* BASE CONSTRUCTOR
*/
function TW_base()
{
global $tw_tag_relations;
$this->stack();
$this->tags = &$tw_tag_relations;
$this->content_off = 0;
}
/*
* BASE "DESTRUCTOR" (parser call this function if end of string is rached)
*/
function base_end()
{
while( $tag = $this->stack_pop() )
{
$this->out .= $this->output->close( $tag );
}
return null;
}
/********************************************************************************************
* TAG STACK implementation
*/
var $Tstack;
function stack() { $this->Tstack[0] = null; }
function stack_push( $tag ) { array_unshift( $this->Tstack, $tag ); }
function stack_pop() { return array_shift( $this->Tstack ); }
function stack_search( $tag ) { return in_array( $tag, $this->Tstack ); }
function stack_top( $tag = null )
{
if( $tag ) return $this->Tstack[0] == $tag;
return $this->Tstack[0];
}
/********************************************************************************************
* S T A R T _ T A G _ f i l t e r <tag atr=value>
*
*/
function START_TAG_filter()
{
$tag = $this->TAG;
if( $tag == null )
{
//###### syntax error
return;
}
if( in_array($tag,$this->config_tags) )
{
//enabled tag
if(is_string($this->config_attr[$tag]))
{
$this->TAG = $tag = $this->config_attr[$tag];
//tag substitution warning
}
// ------- perform attribute check -----------
if($this->is_attributes)
{
if(!$this->config_attr[$tag])
{
$this->ATTRIBUTES = null;
// remove attribute warning
}
else
{
foreach( $this->ATTRIBUTES as $key => $attr )
{
if( ! in_array($attr[0], $this->config_attr[$tag] ) )
{
$this->ATTRIBUTES[$key][1] = null;
// remove attribute warning
continue;
}
// --------- perform value check -----------
if(($val = $attr[1])==null) continue;
if(($cmda = &$this->config[$tag][$attr[0]])==null) continue; //null - accept all values
switch($cmda[0] & 7)
{
case TW_URL:
// V0.1.2: fixed some fatal bugs, big thanx to fczbkk
//
// TODO: make better url check with parse_url()
// $val = strtolower($val);
if(stripos($val, "http://") === false)
{
if(stripos($val, "ftp://") === false)
if(stripos($val, "email:") === false)
if(stripos($val, "https://") === false)
if(stripos($val, "./") === false) // local relative url
$val = "http://".$val;
}
$this->ATTRIBUTES[$key][1] = $val;
break;
case TW_LINK:
//TODO: add link separator check here.
// Do not use this attribute in config!
break;
case TW_NUM:
if( $val >= $cmda[2] && $val <= $cmda[3] ) break;
$this->ATTRIBUTES[$key][1] = $cmda[1];
break;
case TW_CASE:
if( !in_array($val, $cmda[2]) ) $this->ATTRIBUTES[$key][1] = $cmda[1];
break;
}
}
}
}
// check required attributes
if($this->config_req_attr[$tag])
{
if( $this->is_attributes )
foreach( $this->config_req_attr[$tag] as $required )
{
$req_found = 0;
foreach( $this->ATTRIBUTES as $val)
if( $val[0] == $required )
{
$req_found = 1;
break;
}
if($req_found) continue;
switch( $this->config[$tag][$required][0] & 7 )
{
case TW_LINK:
case TW_URL:
// error
break;
default:
$this->ATTRIBUTES[$required] = array($required, $this->config[$tag][$required][1]);
break;
}
}
else
{
foreach( $this->config_req_attr[$tag] as $required )
{
switch( $this->config[$tag][$required][0] & 7 )
{
case TW_LINK:
case TW_URL:
// required tag error
break;
default:
$this->ATTRIBUTES[$required] = array($required, $this->config[$tag][$required][1]);
break;
}
}
}
}
// cross tag removal algorithm
$flag = $this->tags[$tag];
$top = $this->stack_top();
if( $flag[2] != null ) // check if tag before is specified
{
// yes, tag before is specified, check relationship
if(! in_array($top, $flag[2]) )
{
if( $flag[0] & TW_OPT )
{
if( $top == $tag )
{
// End Tag is optional and current tag is the same as last tag (on stack).
// Close previos for XHTML compatibility and open new the same tag.
// Return, because no manipulation with stack is required.
$this->out .= $this->output->close( $tag );
$this->out .= $this->output->pair( $tag, $this->ATTRIBUTES );
return;
}
if( $this->stack_search($tag) )
{
// repair stack
while(($top = $this->stack_pop() ) != $tag )
{
/*if( !($this->tags[$top][0] & TW_OPT ) )
{
// auto close warning
}*/
$this->out .= $this->output->close( $top );
}
$this->stack_push($tag);
$this->out .= $this->output->close( $tag );
$this->out .= $this->output->pair( $tag, $this->ATTRIBUTES );
return;
}
}
// <th>...<td> =>> <th>...</th><td>... (<th> & <td> they have common parent tag (<tr>) )
if( $this->tags[$top][0] & TW_OPT )
{
if( $this->tags[$top][2] == $flag[2] )
{
$this->out .= $this->output->close( $this->stack_pop() );
$this->out .= $this->output->pair( $tag, $this->ATTRIBUTES );
$this->stack_push( $tag );
return;
}
}
// invalid relation between tags #######
return;
}
// valid relationship
}
if( $flag[0] & TW_NOP )
{
//tag without End Tag <br />
$this->out .= $this->output->single( $tag, $this->ATTRIBUTES );
}
else
{
//Tag with End Tag, push to stack
if( ($top == $tag) && ($flag[0] & TW_OPT) )
{
// '<p><p>' => '<p></p><p>'
$this->out .= $this->output->close( $tag );
$this->out .= $this->output->pair( $tag, $this->ATTRIBUTES );
return;
}
$this->out .= $this->output->pair( $tag, $this->ATTRIBUTES );
$this->stack_push( $tag );
}
return;
}
else
{
//disabled tag (or not supported) ######
return;
}
}
/********************************************************************************************
* E N D _ T A G _ f i l t e r </tag>
*
*/
function END_TAG_filter()
{
$tag = $this->TAG;
if( $tag == null )
{
// </>
if ($tag = $this->stack_pop() )
{
$this->out .= $this->output->close($tag);
return;
}
else
{
// kunda underflow :)
return;
}
}
if(in_array($tag,$this->config_tags))
{
// enabled tag
if(is_string($this->config_attr[$tag]))
{
$this->TAG = $tag = $this->config_attr[$tag];
//tag substitution warning
}
$top = $this->stack_top( $tag );
if( $top )
{
$this->out .= $this->output->close( $tag );
$this->stack_pop();
return;
}
if( $this->stack_search($tag) )
{
// closing cross tags
while( ($top = $this->stack_pop()) != $tag )
{
/*if( !($this->tags[$top][0] & TW_OPT ) )
{
// auto close warning
}*/
$this->out .= $this->output->close( $top );
}
$this->out .= $this->output->close( $tag );
}
else
{
// ###### cross tag error
return;
}
}
// drop out warning
}
/*
* THERE ARE STATE IN, OUT & NEW FUNCTIONS
*
*/
/********************************************************************************************
* STATE T_begin '<'
*/
function T_begin_in()
{
$this->tag_position = $this->pti;
$this->TAG = null;
}
// --- MAIN TAG-FILTER FUNCTION ---
function T_begin_out($word)
{
$this->START_TAG_filter();
}
/********************************************************************************************
* STATE TC_begin '</'
*/
function T_Cbegin_in()
{
$this->tag_position = $this->pti;
$this->TAG = null;
}
// TAG CLOSE function
//
function T_Cbegin_out($word)
{
$this->END_TAG_filter();
}
/********************************************************************************************
* STATE T_gettag 'tagname'
*/
function T_gettag_in()
{
$this->is_attributes = 0;
$this->ATTRIBUTES = null;
return;
}
function T_gettag_new($word) { $this->TAG = strtolower($word); }
function T_gettag_out($word) { return; }
/********************************************************************************************
* STATE A_begin '__atr...'
*/
function A_begin_in() { $this->ATTR = null; }
function A_begin_new($word) { $this->ATTR[0] = strtolower($word); }
function A_begin_out($word)
{
if( $this->is_attributes )
{
foreach( $this->ATTRIBUTES as $akey => $aval )
{
if($this->ATTR[0] == $aval[0])
{
// duplicate warning
$this->ATTRIBUTES[$akey] = $aval;
return;
}
}
}
else
{
$this->is_attributes = 1;
}
$this->ATTRIBUTES[] = $this->ATTR;
}
/********************************************************************************************
* STATE V_begin 'atr...'
*/
function V_begin_in() { $this->VALUE = null; }
function V_begin_out($word) { $this->ATTR[1] = $this->VALUE; }
/********************************************************************************************
* STATE VALUE1
*/
function VALUE1_in() { return; }
function VALUE1_out($word) { $this->VALUE = substr($word,0,strlen($word)-1); }
/********************************************************************************************
* STATE VALUE2
*/
function VALUE2_in() { return; }
function VALUE2_out($word)
{
$this->VALUE = str_replace("\"", "&#34", substr($word, 0, strlen($word)-1) );
}
/********************************************************************************************
* STATE VALUE3
*/
function VALUE3_in() { return; }
function VALUE3_out($word) { $this->VALUE = $word; }
} // END class TW_base
?>