tags:

views:

589

answers:

3

Hi Community,

there is a project which I need to extend. All classes are in seperate files, I need to extend some of the classes without rewriting existing code in other files. My idea was to use namespaces but I fail. Here is an example:

I've renamed the original A.php class file to A_Original.php:

class A
{

    public function hello()
    {
     echo "hello world from Class A\n";
    }

}

Then created a new A.php:

namespace AOriginal {

    include 'A_Original.php';
}


namespace {

class A
{

    public function hello()
    {
     echo "hello world from Class A Extended\n";
    }

}

}

This fails because on including the original A_Original.php file the class is dumped to the global scope (thus ignoring the namespace command). I can not modify the existing code inthe A_Original.php file, but renaming is ok.

The other project files (whic I cannot modify) use a require "A.php".

How to accomplish this?

A: 

You can extend a class without modifying its existing behaviour:

class A {
  public function foo(){

  }
}

class MySubClassOfA extends A {
  public function bar(){

  }
}

You can add your own methods to MySubClassOfA, i.e. bar(). You can call the foo method on MySubClassOfA and it's behaviour is the same, unless you define a method called foo in MySubClassOfA.

David Caunt
If I'm not mistaken cydo wants something like Ruby's `extend` or `include`. _Existing_ code that instantiates an object of class A gets the changed/enhanced version without changing that code base.
VolkerK
Ah - this sounds dangerous
David Caunt
VolkerK: right. that's what I need.
cydo
A: 
cydo
does not work if a_original.php class is based on another class<code>class A extends SomeOtherClass</code> because now this other class should be in the same namespace - which isn't on including("someotherclass.php") - its in the global scope. aaargs. I'm lost.include() should not change the current namespace - but it does!
cydo
A: 

I guess that you have no choice but add the single line of "namespace xxx;" code on top of all your files. The following PHP CLI script may be useful.

<?php
function convert($namespace, $srcdir, $dstdir)
{
  try
  {
    $files = glob("$srcdir/{*,.*}", GLOB_BRACE);

    if ( ! file_exists($dstdir) && ! mkdir($dstdir) )
    {
      throw new Exception("Cannot create directory {$dstdir}");
    }

    if ( ! is_dir($dstdir) )
    {
      throw new Exception("{$dstdir} is not a directory");
    }

    foreach ( $files as $f )
    {
      extract(pathinfo($f)); // then we got $dirname, $basename, $filename, $extension

      if ( $basename == '.' || $basename == '..' )
      {
        continue;
      }

      if ( is_dir($f) )
      {
        $d = $dstdir. substr($f, strlen($srcdir));
        convert($namespace, $f, $d);
        continue;
      }

      print "processing {$f} ... ";

      if ( ($s = file_get_contents($f)) === FALSE )
      {
        throw new Exception("Error reading $f");
      }

      if ( preg_match("/^\s*namespace\s+\S+;/m", $s) )
      {
        print "already has namespace, skip";
      }
      else
      {
        $lines   = preg_split("/(\n|\r\n)/", $s);
        $output  = array();
        $matched = FALSE;

        foreach ( $lines as $s )
        {
          $output[] = $s;

          // check if this is a PHP code?
          if ( ! $matched && preg_match('/<(\?(php )*|%)/', $s) )
          {
            $matched = TRUE;
            print "insert namespace ... ";
            $output[] = "namespace {$namespace};";
          }
        }

        if ( file_put_contents("{$dstdir}/{$basename}" , implode("\n", $output)) === FALSE )
        {
          throw new Exception("Cannot save file {$dstdir}/{$basename}");
        }

        if ( ! $matched )
        {
          print ("not a PHP file, skip.");
        }
        else
        {
          print "done!";
        }
      }

      print "\n";
    }
  }
  catch (Exception $e)
  {
    print 'Error: '. $e->getMessage() .' ('. $e->getCode() .')' ."\n";
  }
}

extract($_SERVER);

if ( $argc < 4 )
{
?>
Usage: php -F <?=$argv[0]?> <namespace> <source_dir(s)> <dst_dir>
Convert PHP code to be namespace-aware
<?
  return;
}
else
{
  for ( $i = 2; $i < $argc - 1; $i++ )
  {
    convert($argv[1], $argv[$i], $argv[$argc-1]);
  }
}
?>