How can this layered PHP architecture be improved? This is my first "large scale" system I've designed for myself. Since I'm more of a front end developer than backend developer, I'm not sure if my architecture is sound.

I've created a Factory class which I use to create all my classes and connect to the DB. I think I've tried to use used Singelton(?) pattern, but I'm not really sure.

It probably would be easier if you knew more about the system, but in short it's an online directory mixed with user interaction and social media.

It would be great if I could get some feedback on this.

My factory class

include_once('abc.class.php');

class Factory {
  // One function for each class I include
  // This is the only place I include class files
  function new_abc_obj($id = NULL) { return new Abc(Conn::get_conn(), $id); }  
}


My DB connection class

class Conn {
  private static $conn = NULL;

  /* Establish PDO DB connection */
  private static function init() {
      $conf = self::config();
      try { 
        self::$conn = new PDO($conf['dsn'], $conf['user'], $conf['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND =&gt; "SET NAMES utf8"));
      } 
      catch (PDOException $e) {
        // We remove the username if we get [1045] Access denied
        if (preg_match("/\b1045\b/i", $e-&gt;getMessage())) 
          echo "SQLSTATE[28000] [1045] Access denied for user 'name removed' @ 'localhost' (using password: YES)";
        else
          echo $e-&gt;getMessage();  
      }
  }

  /* Get DB connection */
  public static function get_conn() {
    if (!self::$conn) { self::init(); }
    return self::$conn;
  }

  /* Get username, password and db data from config.php */
  private static function config() {
    $conf = array();

    $conf['user']    = DB_USER;
    $conf['pass']    = DB_PASSWORD;
    $conf['dsn']     = 'mysql:dbname='.DB_NAME.';host='.DB_HOST;

    return $conf;
  }  
}


My class interface

interface iAbc {
  public function one();
  public function two();
}


My Business Logic Layer (BLL)

class Abc extends AbcDAO implements iAbc {
  private $data = array();
  protected $db;

  function __construct(&amp;$db, $id) {
    $this-&gt;db = &amp;$db;

    if($id) {
      $this-&gt;setData($id);
    }
  }

  // __set, __get, __isset is here

  public function one(){};
  public function two(){
    $result = parent::get_some_data();
    return $result;
  };
}


My Data Access Layer (DLL)

class AbcDAO {
  protected function get_some_data($name){
    $sql ="SELECT 123;";
    $stmt = $this-&gt;db-&gt;prepare($sql);
    $stmt-&gt;bindParam(':name', $name, PDO::PARAM_STR);
    $stmt-&gt;execute();
    $result = $stmt-&gt;fetchAll();
    return $result;
  }
}


And this is how I create the object:

$my_abc = Factory::new_abc_obj();
$test = $my_abc-&gt;two();
Factory

Three things about this:


Consider using an autoloader (see PRS-0). Then you don't have to bother about, where files get included.
A Factory is used to create a set of objects, which depends on some context. For example, you could have a Filter and a Renderer for XHTML or HTML5. In order not having to check which one is needed again and again, you have an XhmtlFactory and an Html5Factory with createFilter() and createRenderer() methods.
Factory methods usually are named create....


In your context, a Factory is not helping you.

DB Connection

Don't use static methods, if you can avoid it. Inject dependencies whereever possible. Do not (finally) handle errors within your classes. Leave that up to the caller. The method to get an instance is usually called getInstance() (and is one of the few cases, where static calls are reasonable). The initialization is then made in a private constructor.

class Conn
{
    private $conn = null;

    /**
     * Establish PDO DB connection
     */
    private function __construct(Config $conf)
    {
        try { 
            self::$conn = new PDO($conf-&gt;dsn, $conf-&gt;user, $conf-&gt;pass, array(PDO::MYSQL_ATTR_INIT_COMMAND =&gt; "SET NAMES utf8"));
        } catch (PDOException $e) {
            $message = $e-&gt;getMessage();
            // We remove the username if we get [1045] Access denied
            if (preg_match('/\b1045\b/i', $message)) {
                $message = "SQLSTATE[28000] [1045] Access denied for user 'name removed' @ 'localhost' (using password: YES)";
            }
            throw new ErrorException($message);
        }
    }

    /**
     * Get DB connection
     */
    public static function getInstance(Config $conf)
    {
        if (is_null($this-&gt;$conn)) {
            $this-&gt;conn = new Conn($config);
        }
        return $this-&gt;conn;
    }
}

class Config
{
    public $dsn  = 'mysql:dbname=' . DB_NAME . ';host=' . DB_HOST;
    public $user = DB_USER;
    public $pass = DB_PASSWORD;
}  


To make the singleton pattern in Conn complete, the magic method __clone() should made private, so the connection cannot be copied.

Business Logic Layer

Keep the business and data access layer separated. The business layer ideally does not know anything about storage. The data access layer is injected into the business layer.

class Abc implements iAbc
{
    private $data = array();
    protected $dal;

    public function __construct($dal, $id = null)
    {
        $this-&gt;dal = $dal;

        if (!is_null($id)) {
            $this-&gt;setData($id);
        }
    }

    // __set, __get, __isset is here

    public function one(){};

    public function two()
    {
        $name = 'foo';
        $result = $this-&gt;dal-&gt;get_some_data($name);
        return $result;
    };
}


Data Access Layer

class AbcDao
{
    private $db = null;

    public function __construct(Conn $db)
    {
        $this-&gt;db = $db;
    }

    public function get_some_data($name)
    {
        $sql ="SELECT 123;";
        $stmt = $this-&gt;db-&gt;prepare($sql);
        $stmt-&gt;bindParam(':name', $name, PDO::PARAM_STR);
        $stmt-&gt;execute();

        $result = $stmt-&gt;fetchAll();
        return $result;
    }
}


Controller

In your controller, you now really have control.

$config = new Config;        // You could use any other set of config data
$conn   = new Conn($config); // You could use any other storage method
$my_abc = new Abc($conn);
$test   = $my_abc-&gt;two();


or, shorter

$my_abc = new Abc(new Conn(new Config));
$test   = $my_abc-&gt;two();