Quote from: splittingred at Jul 30, 2008, 08:47 AM
Quote from: BobRay at Jul 30, 2008, 05:30 AM
at some point, somebody needs to explicate the MODx directory tree in plain English to explain what these things are and what they do.
I could work on this.
In the meantime, I explain what controllers/connectors/models are in this series:
http://svn.modxcms.com/docs/display/~splittingred/2008/06/25/PHP+Coding+in+MODx+Revolution%2C+Pt.+I
Yes, that’s excellent, but as I understand it, it’s mainly about how to do programatically what get’s done in the Manager. What I’d like to see is some more on how Revolution interacts with the user on the front end in the normal course of business with references to the corresponding code.
As a further example or being lost and frustrated, after posting my comment last night, I thought I’d go take a look at the new parser Jason mentioned. I figured it *had* to be part of the core, but looking at the directories under that (cache, config, docs, error, export, import, lexicon, model, packages, xpdo), none of them seem to be a likely place for it. I looked into all of them briefly, just in case. Then I looked under connecters but nothing there seemed to fit either. It couldn’t possibly be in the manager section or the setup section, I thought, because that wouldn’t make any sense. I looked in assets, but there’s nothing there but snippets. Then I did a search of the whole of Revolution. I found no hits for "class parser" "document parser" or "resource parser" and 413 hits for "parser." That led me to this in the modx class (under core/model/modx) :
function getParser() {
return $this->getService('parser', 'modParser');
}
which led to this:
function getService($name, $class= '', $path= '', $params= array ()) {
$service= null;
if (!isset ($this->services[$name]) || !is_object($this->services[$name])) {
if (empty ($class) && isset ($this->config[$name . '.class'])) {
$class= $this->config[$name . '.class'];
} elseif (empty ($class)) {
$class= $name;
}
if ($className= $this->loadClass($class, $path, false, true)) {
if ($service= & new $className ($this, $params)) {
$this->services[$name]= $service;
$this->$name= & $this->services[$name];
}
}
}
if (isset ($this->services[$name])) {
$service= & $this->services[$name];
} else {
$this->_log(MODX_LOG_LEVEL_ERROR, "Problem getting service {$name}, instance of class {$class}, from path {$path}, with params " . print_r($params, true));
}
return $service;
}
which led me to this:
which appeared to be a dead end.
I did see some hints that I might find the parser in the XPDO section, but by that time, I was exhausted. Later, I found the parser

(with a search for "parser class") back near where I started in core/model/modx modparser.class.php where it appears most of the MODx classes are (calling that directory "modx-classes" might make sense). At any rate, a new user wouldn’t know that he or she was looking for "modparser" or where to find it. I’m sure there’s a point where all this falls into place but I’m not there yet and I’ve spent some time at it.
I don’t want to be misunderstood, the new Manager is fantastic and finding what I want and doing what I need to do in the Manager has been mostly a breeze.
I’ve wandered around in the mostly uncommented code for hours with no idea what I was looking at or what it did or where to find what I wanted
I feel the code is pretty darn commented. What’s not, so we can augment it?
It is and it isn’t. In retrospect, the code is much better commented overall than I suggested and far better than most code I’ve worked with.
The purpose of each function or class is usually clear (if a little terse) and the purpose of each argument is almost always commented. There’s often little or nothing, though, on how the code in the section does it’s job, how it’s related to other parts of MODx, and where to find the parts it communicates with.
These functions in the modparser class are a good example:
function modParser(&$modx) {
$this->__construct($modx);
}
function __construct(&$modx) {
$this->modx= & $modx;
}
function collectElementTags($origContent, & $matches, $prefix= '[[', $suffix= ']]') {
$matchCount= 0;
if (!empty ($origContent) && is_string($origContent) && strpos($origContent, $prefix) !== false) {
$openCount= 0;
$offset= 0;
$openPos= 0;
$closePos= 0;
if (($startPos= strpos($origContent, $prefix)) === false) {
return $matchCount;
}
$offset= $startPos +strlen($prefix);
if (($stopPos= strrpos($origContent, $suffix)) === false) {
return $matchCount;
}
$stopPos= $stopPos + strlen($suffix);
$length= $stopPos - $startPos;
$content= $origContent;
while ($length > 0) {
$openCount= 0;
$content= substr($content, $startPos);
$openPos= 0;
$offset= strlen($prefix);
if (($closePos= strpos($content, $suffix, $offset)) === false) {
break;
}
$nextOpenPos= strpos($content, $prefix, $offset);
while ($nextOpenPos !== false && $nextOpenPos < $closePos) {
$openCount++;
$offset= $nextOpenPos + strlen($prefix);
$nextOpenPos= strpos($content, $prefix, $offset);
}
$nextClosePos= strpos($content, $suffix, $closePos + strlen($suffix));
while ($openCount > 0 && $nextClosePos !== false) {
$openCount--;
$closePos= $nextClosePos;
$nextOpenPos= strpos($content, $prefix, $offset);
while ($nextOpenPos !== false && $nextOpenPos < $closePos) {
$openCount++;
$offset= $nextOpenPos + strlen($prefix);
$nextOpenPos= strpos($content, $prefix, $offset);
}
$nextClosePos= strpos($content, $suffix, $closePos + strlen($suffix));
}
$closePos= $closePos +strlen($suffix);
$outerTagLength= $closePos - $openPos;
$innerTagLength= ($closePos -strlen($suffix)) - ($openPos +strlen($prefix));
$matches[$matchCount][0]= substr($content, $openPos, $outerTagLength);
$matches[$matchCount][1]= substr($content, ($openPos +strlen($prefix)), $innerTagLength);
$matchCount++;
if ($nextOpenPos === false) {
$nextOpenPos= strpos($content, $prefix, $closePos);
}
if ($nextOpenPos !== false) {
$startPos= $nextOpenPos;
$length= $length - $nextOpenPos;
} else {
$length= 0;
}
}
}
if ($this->modx->getDebug() === true && !empty($matches)) {
$this->modx->_log(MODX_LOG_LEVEL_DEBUG, "modParser::collectElementTags \$matches = " . print_r($matches, 1) . "\n");
// $this->modx->cacheManager->writeFile(MODX_CORE_PATH . 'logs/parser.log', print_r($matches, 1) . "\n", 'a');
}
return $matchCount;
}
This isn’t a very good example because very few people actually need to know how this works, but I happened to have it handy.
Ok now, everyone who wishes I had put all the time I spent complaining and writing this diatribe into actually contributing to MODx raise his or her hand.

(mine is up)