<?php
/**
 * The base of all Sitemap.org Builder's, handles generic functionality.
 *
 * @author Damian Wood (damianwøød@msn.cøm, replace ø's)
 */
abstract class AbstractSitemapBuilder
{
    protected 
/*final*/ $absoluteSiteUri;
    protected 
$addCount 0;
    protected 
$currentStream;
    
    
/**
     * Construct a new instance.
     *
     * @param string $absoluteSiteUri the URI of the site, eg: "http://www.example.com/".
     */
    
public function __construct($absoluteSiteUri)
    {
        if(
$absoluteSiteUri == null)
        {
            if(
php_sapi_name() != 'cli')
                
$absoluteSiteUri 'http://'.$_SERVER[HTTP_HOST].'/';
             
             else
                throw new 
Exception('You must specify $absoluteSiteUri when in CLI mode');
        }
        elseif(
$absoluteSiteUri[strlen($absoluteSiteUri) - 1] != '/')
            throw new 
Exception('You must end $absoluteSiteUri with a "/"');
            
        
$this->absoluteSiteUri $absoluteSiteUri;
    }
    
    
/**
     * Destruct the current instance by calling endSitemap().
     */
    
public function __destruct()
    {
        
$this->endSitemap();
    }

    
/**
     * Add a URI to the sitemap.
     *
     * @param string $path a path relative to $absoluteSiteUri, eg: "product/123.html" would be
     * translated into a sitemap entry of "http://www.example.com/product/123.html".
     */
    
public function add($path)
    {
        
fwrite($this->currentStream,
            
'<url><loc>'
            
.htmlentities($this->absoluteSiteUri.$path)
            .
"</loc></url>\n");
        
        
$this->addCount++;
    }

    
/**
     * Write the opening tags.
     */    
    
protected function startSitemap()
    {
        
fwrite($this->currentStream,
            
'<?xml version="1.0" encoding="UTF-8"?>'
            
."\n"
            
.'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
            
."\n");    
    }    

    
/**
     * Write the closing tag and close.
     */
    
protected function endSitemap()
    {
        
fwrite($this->currentStream'</urlset>');
        
fclose($this->currentStream);    
    }
}

/**
 * Builds single file sitemap's. By default this will write to the php output stream.
 *
 * @author Damian Wood (damianwøød@msn.cøm, replace ø's)
 */
class SingleSitemapBuilder extends AbstractSitemapBuilder
{
    const 
STREAM_URI_PHP_OUTPUT 'php://output';
    protected 
/*final*/ $streamUri;
    
    
/**
     * Construct a new instance
     *
     * @param string $streamUri (optional) the registered stream URI where the sitemap will be written to, eg: a file path.
     */
    
public function __construct($absoluteSiteUri null,
        
$streamUri self::STREAM_URI_PHP_OUTPUT)
    {
        
$this->streamUri $streamUri;
        
parent::__construct($absoluteSiteUri);
        
        if(
$this->streamUri == self::STREAM_URI_PHP_OUTPUT)
            
header('Content-Type: application/xml');
    }
    
    public function 
add($path)
    {
        if(
$this->addCount == 0)
        {
            
$this->currentStream fopen($this->streamUri'w');
            
parent::startSitemap();
        }    
        
        
parent::add($path);
    }
}

/**
 * Builds a multiple file sitemap that uses a "sitemap index file" file to reference the actual sitemaps.
 *
 * @author Damian Wood (damianwøød@msn.cøm, replace ø's)
 */
class IndexedSitemapBuilder extends AbstractSitemapBuilder
{
    const 
DEFAULT_SITEMAP_DIR '';
    const 
DEFAULT_SITEMAP_URL_LIMIT 50000;
    const 
DEFAULT_SITEMAPINDEX_FILENAME 'sitemap.xml';
    protected 
/*final*/ $sitemapDir;
    protected 
/*final*/ $sitemapUrlLimit;
    protected 
/*final*/ $hasZlibStreamWrapper;
    protected 
$sitemapCount 0;

    
/**
     * Construct a new instance.
     *
     * @param string $sitemapDir (optional) the path of the directory where the sitemap files will be created, eg: "sitemap/".
     * @param string $sitemapUrlLimit (optional)  the number of URI's per sitemap, the specification says 50000 is the maximum.
     */
    
public function __construct($absoluteSiteUri,
        
$sitemapDir self::DEFAULT_SITEMAP_DIR,
        
$sitemapUrlLimit self::DEFAULT_SITEMAP_URL_LIMIT)
    {
        if(
$sitemapDir != self::DEFAULT_SITEMAP_DIR)
        {
            if(!
is_dir($sitemapDir) || !is_writable($sitemapDir))
                throw new 
Exception('Cannot write to sitemap directory:'.$sitemapDir);
            
            if(
$sitemapDir[strlen($sitemapDir) - 1] != '/')
                throw new 
Exception('You must end $sitemapDir with a "/"');
        }
        
        
$this->sitemapDir $sitemapDir;
        
$this->sitemapUrlLimit $sitemapUrlLimit;    
        
$this->hasZlibStreamWrapper in_array('compress.zlib'stream_get_wrappers());
        
        
parent::__construct($absoluteSiteUri);
    }
    
    public function 
add($path)
    {
        if(
$this->addCount $this->sitemapUrlLimit == 0)
        {
            if(
$this->sitemapCount 0)
                
parent::endSitemap();
            
            
$path $this->sitemapDir.'sitemap_'.$this->sitemapCount.'.xml';
            
            if(
$this->hasZlibStreamWrapper)
                
$path 'compress.zlib://'.$path.'.gz';
            
            
$this->currentStream fopen($path'w');
            
parent::startSitemap();
            
            
$this->sitemapCount++;
        }
        
        
parent::add($path);
    }
    
    
/**
     * Create the "sitemap index file" which references the actual sitemaps.
     *
     * @param string $filename (optional) the file name for the sitemap index file. The default is "sitemap.xml".
     */
    
public function createIndex($filename self::DEFAULT_SITEMAPINDEX_FILENAME)
    {
        
$sitemapIndexFileHandle fopen($this->sitemapDir.$filename'w');
        
fwrite($sitemapIndexFileHandle,
            
'<?xml version="1.0" encoding="UTF-8"?>'
            
."\n"
            
.'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
            
."\n");
        
        for(
$i 0$i $this->sitemapCount$i++)
        {
            
$uri $this->absoluteSiteUri.$this->sitemapDir.'sitemap_'.$i.'.xml';
            
            if(
$this->hasZlibStreamWrapper)
                
$uri .= '.gz';
            
            
fwrite($sitemapIndexFileHandle,
                
'<sitemap><loc>'
                
.htmlentities($uri)
                .
"</loc></sitemap>\n");
        }
        
        
fwrite($sitemapIndexFileHandle'</sitemapindex>');
        
fclose($sitemapIndexFileHandle);
    }
}
?>