Refactoring a class hierarchy to remove subclasses
Edit: As David pointed out in the comments, I am completely wrong about constructors in the abstract classes. I hid the (ir)relevant section to avoid confusion and attempted to edit the rest of the post so it fit the new context. In case I missed something, Abstract classes CAN have constructors because they CAN'T be instantiated therefore any references to the init() method that may be left over should be assumed to be parent::__construct() instead. Thank you again David.


  Abstract classes can not, or rather should not, be instantiated. Therefore they should never have a constructor. If you want to have a shared "constructor" there are a couple different things you can do. The easiest method is to create an init()-like function and call that in your real constructors.  Another method is to extend an actual class. Since you don't seem to be trying to instantiate this class, as some who use "abstract constructors" are wont to do, then I think the first solution should be adequate, though I should add that the init() method should not be public to avoid "accidental" reinstantion.

    protected function init() {
        $this->_result = new ParserResult();
    }


Abstract classes are still classes. Therefore anything defined in it is still defined in its children. Thus, redefining properties should actually be handled in construction, because, if I'm remembering this correctly, redefining them in the children actually throws warnings. Either way, the better place is in construction.

public function __construct() {
    $this-&gt;_invalidSegmentMessage = 'Invalid GS segment: ';
    $this-&gt;_segmentRegex = "/^GS\*IN\*\w{2,15}\*\w{2,15}\*(\d{6}|\d{8})\*\d{4}\*\d{1,9}\*X\*\d{1,12}$/";

    parent::__construct();
}


So, there are a few principles being violated in your createSegmentObject() method. The first, and most obvious, is the "Don't Repeat Yourself" (DRY) principle. This can be observed by how similar all these methods are. If we take into account the Inversion of Control (IoC) Principle as well, then we can see that it is violating both of these principles. IoC is something I've never really thought about before, so I don't feel entirely comfortable explaining it. Google it if you're not sure what it is, I'll be reading up on it more myself after this post. I think you began to notice something was amiss, which is why you added it to the abstract class, you just didn't go quite far enough with it. The third principle, which is making this a little more difficult to see, is that this method also violates the Single Responsibility Principle. This method should be concerned with creating a segment, not creating AND setting. Leave that for after you call the method. If we try setting it immediately then we can't extend it.

//abstract class
protected function createSegmentObject( Segment $segment ) {
    $elements = explode('*', segment);

    $segment-&gt;setSupplierId( $elements[ self::SUPPLIER_ID_INDEX ] );
    $this-&gt;segment = $segment;
    $this-&gt;elements = $elements;
}

//extending class
protected function createSegmentObject( Segment $segment ) {
    parent::createSegmentObject( $segment );

    $this-&gt;segment-&gt;setGroupReference( $this-&gt;elements[ self::GROUP_HEADER_INDEX ] );
}

//usage
if ($this-&gt;isValidSegment()) {
    $this-&gt;createSegmentObject();
    $this-&gt;_result-&gt;setSegment( $this-&gt;segment );
}


In case you are curious, createSegmentObject( Segment $segment ) is assuming that your "segments" are using a common interface or abstract class and that it is called "Segment". If you are not using one of these, then I would seriously think about doing so. Assuming for now that you are, then we can declare the parent class as the common denominator and type hint our parameters for it. This will ensure that any object passed to the above method must be a type of Segment and will be able to produce the expected results every time, else PHP will throw errors.

So, we've just eliminated most of the need for multiple classes. The only thing holding us back are these unique properties and the occasional extensions. The interesting thing about these classes is that the properties are very structured and unique. So what do we do when we have a common data structure that needs to be saved and reused? If we create a database with each row denoting a segment and each field a property, then we can construct our segments by calling that specific row in the database. Of course, we should inject these parameters into our constructor to avoid our class becoming dependent. I'll leave the database implementation to you, but here's the constructor:

public function __construct( $id, $group, $type, $regex ) {
    $this-&gt;_supplierIdIndex = $id;
    $this-&gt;_groupHeaderIndex = $group;
    $this-&gt;_invalidSegmentMessage = "Invalid $type segment: ";
    $this-&gt;_segmentRegex = $regex;

    parent::_construct();
}


That leaves us with just extensions holding us back now. We could create an elaborate comparison table and pick and choose when to use what, or we can automate the process and use a factory pattern. Again, I'm going to leave this up to you, for multiple reasons, but the main one being that I haven't played with factories yet and wouldn't be sure where to begin. Besides, without the rest of the subclasses I couldn't begin.

I believe some of this is what Saul linked to, but I just skimmed over it. I did spy something about a factory in there, so maybe much of this is the same, but it lacked a lot of the explanations that I felt were necessary. Hopefully this helps.