class XmlSaxClass {
private $file_path;
private $encoding;
private $output = array();
private $element = null;
private $full_path_elements = array();
private $necessary_elements = array();
private $branch_elements = array();
private $deep;
public function __construct($file_path, $encoding = 'UTF-8'){
$this -> encoding = $encoding;
$this-> file_path = $file_path;
}
public function execute($necessary_elements, $full_path_elements){
$this -> necessary_elements = $necessary_elements;
$this -> full_path_elements = $full_path_elements;
$parser = xml_parser_create($this -> encoding);
xml_set_object($parser, $this);
xml_set_element_handler($parser, 'startElements', 'endElements');
xml_set_character_data_handler($parser, "characterData");
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
$handle = fopen($this-> file_path, "r");
$base_memory_usage = memory_get_usage();
while ($data = fread($handle, 4096)) {
xml_parse($parser, $data, feof($handle));
echo("<br>");
print(memory_get_usage() - $base_memory_usage);
}
xml_parser_free($parser);
return $this -> output;
}
private function startElements($parser, $name, $attrs){
$this -> deep++;
if(!empty($name)) {
if($this -> deep == 1){
if ($name == $this -> full_path_elements[0]){
$this -> branch_elements[0] = $name;
}
}
else{
$this -> cut_elements_in_branch_if_down();
$this -> push_element_to_branch($name);
}
$branch_elements_count = count($this -> branch_elements);
if($branch_elements_count == count($this -> full_path_elements)){
if($this -> deep == $branch_elements_count){
$this -> output[] = array();
}
elseif (in_array($name, $this -> necessary_elements)){
$this -> element = $name;
}
}
}
}
private function endElements($parser, $name){
$this -> deep --;
if(!empty($name)) {
$this -> element = null;
}
}
private function characterData($parser, $data){
if(!empty($data)) {
if (in_array($this -> element, $this -> necessary_elements)) {
$this ->output[count($this -> output)-1][$this -> element] = trim($data);
}
}
}
private function cut_elements_in_branch_if_down(){
if (count($this -> branch_elements) > $this -> deep -1 ){
$this -> branch_elements = array_slice($this -> branch_elements, 0, $this -> deep -1);
}
}
private function push_element_to_branch($name){
if (count($this -> branch_elements) == (($this -> deep)-1)){
if (isset($this -> full_path_elements[$this -> deep-1] ) &&
$name == ($this -> full_path_elements[($this -> deep)-1])){
$this -> branch_elements[($this -> deep)-1] = $name;
}
}
}
}
Здесь небольшой простой скрипт. В методе execute происходит разбор xml документа и беруться листья текущей ветки.
Первый параметр список атрибутов, второй это полный путь к нужной ветке xml.
$sax_parser = new \XmlSaxClass($file_path);
$response = $sax_parser->execute(array(
'learner',
'teacher'
), array(
'school',
'class'
));
Поидее я написал методом SAX, но происходит утечка памяти и 1г обработать не может.
Возможно я неправильно понимаю метод SAX?
В теории он разбирается частями не загружая все дерево DOM.
Но видимо я что-то зделал не так.
Скрипт нормально работает на маленьких значениях, на больших валится.
Все echo удаляю конечно, здесь только для примера потери памяти.
Вот очень простой xml
<?xml version="1.0" encoding="UTF-8"?>
<school>
<class>
<learner>Ученик1</learner>
</class>
<class>
<learner>Ученик2</learner>
</class>
<class>
<learner>Ученик3</learner>
<teacher>Училка</teacher>
</class>
</school>