In order to use PEAR modules, they must be on the PHP include_path. Is there some way that libraries can support this?

Comments

pillarsdotnet’s picture

For instance, I wrote the Mail MIME module as a Drupal-friendly wrapper around the PEAR Mail_mime module.

My first inclination was to use Libraries API to track whether Mail_mime is installed. I wrote a hook_requirements() function to perform this check.

Then I realized that Libraries won't detect PEAR modules 'cuz they aren't installed in a Libraries directory. Tried using hook_library() to specify the places where it should look, but realized that I can't because they might be in any location on the include path.

Then I found out that PEAR libraries require_once() each other willy-nilly, so it's a lost cause. Libraries API just doesn't support the include path, nor the concept that required library files may be installed somewhere outside the Drupal site, nor the concept that some library files must be on the include path in order for them to work correctly.

Looked for another project that supports PEAR in D7 and struck out. There are two for D6 but neither one has a D7 version in the works.

Wound up punting by creating a file_default_scheme()://PEAR directory and adding it to the include path with a hook_init() implementation.

If there were some way to accomplish the same task using Libraries API, I'd be willing, even if it makes my code bigger.

tstoeckler’s picture

Sorry, if I'm being stupid, but I don't quite get what the problem is you are having.
Could you give an example of where you would want your library to be located?
Libraries API (7.x-2.x) has the notion of 'libraries path' which you could specify if it can't be one of the libraries folders. But it seems that the exact location doesn't really matter? Again, I don't quite get it yet.

sun’s picture

PEAR libraries are usually installed through the OS package manager, and usually reside somewhere in /usr/share/php or similar.

PHP accesses those libraries through a customized php.ini include_path. Performance-wise, not really the best way to load/include files... but yeah, that's how PEAR libraries are supposed to work.

file_exists() on those include file paths can throw errors or warnings, because the files are outside of the webspace.

I don't really have an idea for this issue yet. I can only guess that the solution will require a custom library detection callback (which literally simply tries a class_exists() or similar to invoke PHP's auto-loading and includes those libraries at runtime [or not])

pillarsdotnet’s picture

Well, for now I'm using @fopen($path, TRUE) to detect whether files exist. The hook_requirements() implementation returns a REQUIREMENT_WARNING on failure to auto-create a directory and REQUIREMENT_ERROR on failure to download, install, or read a file.

function mailmime_requirements($phase) {
  $errors = array();
  $warnings = array();
  $pear = file_directory_path() . '/PEAR';
  $t = get_t();
  if (!file_check_directory($pear,
      FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)
  ) {
    $warnings[] = $t(
      'Could not create directory %pear', array('%pear' => $pear)
    );
    $pear = NULL;
  }
  else {
    set_include_path(
      implode(PATH_SEPARATOR,
        array_unique(
          array_merge(
            array($pear),
            explode(PATH_SEPARATOR, get_include_path())
          )
        )
      )
    );
  }
  $repository = 'http://svn.php.net/repository/pear/';
  // This could be a hook.
  $packages = mailmime_required_PEAR_packages();
  foreach ($packages as $name => $package) {
    $local = $package['local_path'];
    if ($pear) {
      $packagedir = "$pear/$local";
      if (!file_check_directory($packagedir,
        FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
        $failures[] = $t(
          'Could not create directory %local',
          array('%local' => $packagedir)
        );
        continue;
      }
      $remote = $package['remote_path'];
    }
    foreach ($package['files'] as $file) {
      $include = "$local$file";
      // This searches the include_path for $include.
      if ($fd = @fopen($include, 'r', TRUE)) {
        fclose($fd);
        continue;
      }
      if ($pear) {
        $url = "$repository$remote$file";
        $filename = "$pear/$include";
        if ( !($response = drupal_http_request($url))
          || !isset($response->data)
        ) {
          $errors[] = $t(
            'Could not fetch %url.',
            array('%url' => $url)
          );
        }
        elseif (!($count = strlen($response->data))) {
          $errors[] = $t(
            '%url returned zero-length file',
            array('%url' => $url)
          );
        }
        elseif ($count != file_put_contents($filename, $response->data)) {
          $errors[] = $t(
            'Could not write %count bytes to %filename.',
            array('%count' => $count, '%filename' => $filename)
          );
        }
        elseif ($fd = @fopen($include, 'r', TRUE)) {
          fclose($fd);
          continue;
        }
        $errors[] = $t(
          'Installed but could not load %package file %include. '
          . 'Add %pear to your %include_path.'
          array(
            '%package' => "PEAR::$name",
            '%include' => $include,
            '%pear' => $pear,
            '%include_path' => 'include_path',
          )
        );
      }
      else {
        $errors[] = $t(
          'Could not find or download %package file %include.'
          array('%include' => $include, '%package' => "PEAR::$name")
        );
      }
    }
  }
  if (empty($errors) && empty($warnings)) {
    return array();
  }
  return array(
    'MailMIME' => array(
      'title' => $t('Automatic PEAR Mail_Mime package installation'),
      'description' => implode('<br />', $errors + $warnings),
      'severity' => empty($errors) : REQUIREMENT_WARNING : REQUIREMENT_ERROR,
    ),
  );
}
ohnobinki’s picture

+1

The ideal and cleanest way to manage external libraries is to let them be installed by the package manager into something external from Drupal.

This goes for more than just PEAR modules.

pocock’s picture

The README.Debian for the new Libraries API package on Debian provides some clues about this:

http://danielpocock.com/drupal-on-debian-libraries-api