Se posso, queste sono le mie due versioni alternative di cui una alloca sempre spazio e l'altra cerca di risparmiarlo permettendo l'accesso alle proprietà tramite operatore ->. La classe URL costa di due soli metodi e non ha bisogno di metodi d'accesso. L'accesso in scrittura è ovviamente negato.

Versione spendacciona:
Codice PHP:
<?php
class OggettoCostante {
    private 
$iDati = [];

    public function 
__construct(array $valori) {
        foreach (
$valori as $nomeProprieta => $valore) {
            if (
is_object($valore))
                
$valore = clone $valore;
            
$this->iDati[$nomeProprieta] = $valore;
        }
    }

    public function 
__get($name) {
        if (isset(
$this->iDati[$name]))
            return 
$this->iDati[$name];

        throw new 
Exception("La proprieta non e' definita");
    }

    public function 
__set($name$value) {
        throw new 
Exception("Non e' ammessa la modifica dello stato dell'oggetto");
    }

}

final class 
URL extends OggettoCostante {

    public function 
__construct(array $url) {
        if (!isset(
$url['url']) or !filter_var($url['url'], FILTER_VALIDATE_URL, array(FILTER_FLAG_SCHEME_REQUIREDFILTER_FLAG_HOST_REQUIRED))) {
            throw new 
Exception("Parametro non correttamente formattato o url non valido.");
        }

        
$purl parse_url($url['url']);
        
$scheme $purl['scheme'];
        
$host $purl['host'];
        
$path = (isset($purl['path'])) ? $purl['path'] : '';
        
$query = (isset($purl['query'])) ? $purl['query'] : '/';

        
parent::__construct(array('url' => $url['url'], 'scheme' => $scheme'host' => $host'path' => $path'query' => $query));
    }

    public function 
addQuery(array $params) {
        if (empty(
$params))
            throw new 
Exception('I parametri non sono validi');

        
$querystring '';
        foreach (
$params as $key => $value)
            
$querystring .= '&' $key '=' urlencode($value);

        
$querystring = (empty($this->query)) ? trim($querystring'&') : $this->query $querystring;

        
$url $this->scheme '://' $this->host $this->path '?' $querystring;

        return new 
URL(['url' => $url]);
    }

}

$url = new URL(['url' => 'h*t*t*p://www.html.it']);

//aggiungiamo alcuni parametri
$nuovoUrl $url->addQuery(array('uid' => 1'name' => 'Paolo'));

if (
$url == $nuovoUrl
    echo 
'I due "value URL object" sono uguali';
 else 
    echo 
'I due "value URL object" NON sono uguali';

//output: I due "value URL object" NON sono uguali  
?>
Versione che non istanzia nuovi oggetti a fronte del ripresetnarsi di stati già utilizzati:

Codice PHP:
<?php
class OggettoCostante {
    private static 
$gliOggetti = [];
    private 
$iDati = [];

    
/**
     * Crea un nuovo oggetto costante. In $valori vanno specificate coppie
     * [nomeProprietà => valore, ...]. Le proprietà saranno poi accessibili
     * tramite la sintassi $oggetto->nomeProprietà in sola lettaura
     * @param array $valori
     * @return type
     */
    
public static function creaNuovoOggetto(array $valori) {         
        
$hash hash("sha256"serialize($valori)); //tengo le dita incrociate
        
$class get_called_class();
        if (!isset(
self::$gliOggetti[$class][$hash]))
            
self::$gliOggetti[$class][$hash] = static::istanziaOggetto($valori);
        return 
self::$gliOggetti[$class][$hash];
    }
    
    protected static function 
istanziaOggetto(array $valori){
        return new 
OggettoCostante($valori);
    }

    protected function 
__construct(array $valori) {
        foreach (
$valori as $nomeProprieta => $valore) {
            if (
is_object($valore))
                
$valore = clone $valore;
            
$this->iDati[$nomeProprieta] = $valore;
        }
    }

    public function 
__get($name) {
        if (isset(
$this->iDati[$name]))
            return 
$this->iDati[$name];
        throw new 
Exception("La proprieta non e' definita");
    }

    public function 
__set($name$value) {
        throw new 
Exception("Non e' ammessa la modifica dello stato dell'oggetto");        
    }

}

final class 
URL extends OggettoCostante {

    
/**
     * Crea un nuovo oggetto url costante. Il parametro è un array del tipo:
     * ['url' => 'http://www.google.com']
     * @param array $url
     * @throws Exception
     */
    
public static function creaNuovoOggetto(array $url) {
         if (!isset(
$url['url']) or !filter_var($url['url'],  FILTER_VALIDATE_URL, array(FILTER_FLAG_SCHEME_REQUIRED,  FILTER_FLAG_HOST_REQUIRED))) {
            throw new 
Exception("Parametro non correttamente formattato o url non valido.");
        }

        
$purl parse_url($url['url']);
        
$scheme $purl['scheme'];
        
$host $purl['host'];
        
$path = (isset($purl['path'])) ? $purl['path'] : '/';
        
$query = (isset($purl['query'])) ? $purl['query'] : '';
        
        return 
parent::creaNuovoOggetto(array('url' => $url['url'], 'scheme'  => $scheme'host' => $host'path' => $path'query' =>  $query));
    }
    
    protected static function 
istanziaOggetto(array $valori) {
        return new 
URL($valori);
    }

    public function 
addQuery( array $params ) {
        if ( empty( 
$params ) )
            throw new 
Exception'I parametri non sono validi' );        

        
$querystring '';
        foreach ( 
$params as $key=>$value 
            
$querystring .= '&' $key '=' urlencode$value );        

        
$querystring = (empty($this->query))? trim($querystring'&'):$this->query $querystring;

        
$url $this->scheme '://' $this->host $this->path '?' $querystring;

        return 
self::creaNuovoOggetto(['url' => $url]);
    }        
}

$url URL::creaNuovoOggetto( ['url' => 'h*t*t*p://www.html.it']);

//aggiungiamo alcuni parametri
$nuovoUrl $url->addQuery( array( 'uid'=>1'name'=>'Paolo' ) );

if ( 
$url == $nuovoUrl ) {
    echo 
'I due "value URL object" sono uguali';
    } else {
    echo 
'I due "value URL object" NON sono uguali';
}

//output: I due "value URL object" NON sono uguali  
?>
Inutile dire che l'output prodotto è quello atteso. Non capisco perchè non posso postre indirizzi web nel codice e mi tocca aggiungere gli *