(for reference, I'm aware of #522746: Drupal Exception Wrapper Class but that issue was postponed to D8 while I don't think this can be).

I did a short grep to check how we are passing the exception message to thrown exceptions because of #829484-12: Uncaught PDO Exception - XSS to find out if they always end with a point.

What I found is a translation mess ;)

$ grep -R  "throw new " modules/
modules/system/system.api.php:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.archiver.inc:      throw new Exception(t('Cannot open %file_path', array('%file_path' => $file_path)));
modules/system/system.api.php.orig:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/trigger/trigger.admin.inc:        throw new Exception(t('Missing/undefined save action (%save_aid) for %aid action.', array('%save_aid' => $aid, '%aid' => $aid)));
modules/field/modules/field_sql_storage/field_sql_storage.module:    throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
modules/field/modules/field_sql_storage/field_sql_storage.module:      throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
modules/field/tests/field_test.field.inc:    throw new FieldException("field_test 'unchangeable' setting cannot be changed'");
modules/field/tests/field_test.storage.inc:    throw new Exception('field_test_storage_failure engine always fails to create fields');
modules/field/field.attach.inc:    throw new FieldValidationException($errors);
modules/field/field.api.php:      throw new FieldUpdateForbiddenException("Cannot update a list field not to include keys with existing data");
modules/field/field.crud.inc:    throw new FieldException('Attempt to create an unnamed field.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with no type.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with a name longer than 32 characters: %name',
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:      throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field of unknown type %type.', array('%type' => $field['type'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with unknown storage type %type.', array('%type' => $field['storage']['type'])));
modules/field/field.crud.inc:    throw new FieldException('Attempt to update a non-existent field.');
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's type.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's entity_types property.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's storage type.");
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to create an instance of a field @field_name that doesn't exist or is currently inactive.", array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without an entity type.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name on forbidden entity type @entity_type.', array('@field_name' => $instance['field_name'], '@entity_type' => $instance['entity_type'])));
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to update an instance of a nonexistent field @field.', array('@field' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to update an instance of field @field on bundle @bundle that doesn't exist.", array('@field' => $instance['field_name'], '@bundle' => $instance['bundle'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to purge a field @field_name that still has instances.', array('@field_name' => $field['field_name'])));
modules/update/update.manager.inc:    throw new Exception(t('Cannot extract %file, not a valid archive.', array ('%file' => $file)));
modules/update/update.manager.inc:    throw new Exception(t('Unable to extract %file', array('%file' => $archive_file)));
modules/simpletest/tests/error_test.module:  throw new Exception("Drupal is awesome");
modules/simpletest/tests/system_test.module:  throw new Exception('Drupal is <blink>awesome</blink>.');
modules/simpletest/tests/filetransfer.test:        throw new Exception('Error removing fake module directory.');
modules/simpletest/tests/filetransfer.test:      throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
modules/node/tests/node_test_exception.module:    throw new Exception('Test exception for rollback.');
berdir@ubuntu-thinkpad:~/Projekte/d7/drupal$ grep -R  "throw new " modules/ includes/
modules/system/system.api.php:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.archiver.inc:      throw new Exception(t('Cannot open %file_path', array('%file_path' => $file_path)));
modules/system/system.api.php.orig:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/trigger/trigger.admin.inc:        throw new Exception(t('Missing/undefined save action (%save_aid) for %aid action.', array('%save_aid' => $aid, '%aid' => $aid)));
modules/field/modules/field_sql_storage/field_sql_storage.module:    throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
modules/field/modules/field_sql_storage/field_sql_storage.module:      throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
modules/field/tests/field_test.field.inc:    throw new FieldException("field_test 'unchangeable' setting cannot be changed'");
modules/field/tests/field_test.storage.inc:    throw new Exception('field_test_storage_failure engine always fails to create fields');
modules/field/field.attach.inc:    throw new FieldValidationException($errors);
modules/field/field.api.php:      throw new FieldUpdateForbiddenException("Cannot update a list field not to include keys with existing data");
modules/field/field.crud.inc:    throw new FieldException('Attempt to create an unnamed field.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with no type.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with a name longer than 32 characters: %name',
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:      throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field of unknown type %type.', array('%type' => $field['type'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with unknown storage type %type.', array('%type' => $field['storage']['type'])));
modules/field/field.crud.inc:    throw new FieldException('Attempt to update a non-existent field.');
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's type.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's entity_types property.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's storage type.");
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to create an instance of a field @field_name that doesn't exist or is currently inactive.", array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without an entity type.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name on forbidden entity type @entity_type.', array('@field_name' => $instance['field_name'], '@entity_type' => $instance['entity_type'])));
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to update an instance of a nonexistent field @field.', array('@field' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to update an instance of field @field on bundle @bundle that doesn't exist.", array('@field' => $instance['field_name'], '@bundle' => $instance['bundle'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to purge a field @field_name that still has instances.', array('@field_name' => $field['field_name'])));
modules/update/update.manager.inc:    throw new Exception(t('Cannot extract %file, not a valid archive.', array ('%file' => $file)));
modules/update/update.manager.inc:    throw new Exception(t('Unable to extract %file', array('%file' => $archive_file)));
modules/simpletest/tests/error_test.module:  throw new Exception("Drupal is awesome");
modules/simpletest/tests/system_test.module:  throw new Exception('Drupal is <blink>awesome</blink>.');
modules/simpletest/tests/filetransfer.test:        throw new Exception('Error removing fake module directory.');
modules/simpletest/tests/filetransfer.test:      throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
modules/node/tests/node_test_exception.module:    throw new Exception('Test exception for rollback.');
includes/theme.inc:    throw new Exception(t('theme() may not be called until all modules are loaded.'));
includes/install.core.inc:      throw new Exception(install_already_done_error());
includes/install.core.inc:        throw new Exception(implode("\n", $errors));
includes/install.core.inc:      throw new Exception(implode("\n\n", $failures));
includes/install.core.inc:      throw new Exception(install_already_done_error());
includes/install.core.inc:        throw new Exception(install_no_profile_error());
includes/install.core.inc:    throw new Exception(install_no_profile_error());
includes/install.core.inc:        throw new Exception(st('Sorry, you must select a language to continue the installation.'));
includes/install.core.inc:    throw new Exception(st('Sorry, the profile you have chosen cannot be loaded.'));
includes/install.core.inc:    throw new Exception(install_already_done_error());
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exist.", array('%field' => $field, '%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exist.", array('%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exist.", array('%table' => $table, '%name' => $field)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $field, '%name_new' => $field_new)));
includes/database/query.inc:      throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
includes/database/query.inc:      throw new NoFieldsException('There are no fields available to insert with.');
includes/database/query.inc:      throw new InvalidMergeQueryException("You need to specify key fields before executing a merge query");
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exist.", array('%field' => $field, '%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exist.", array('%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exist.", array('%table' => $table, '%name' => $field)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $field, '%name_new' => $field_new)));
includes/database/pgsql/database.inc:          throw new PDOException('Invalid return directive: ' . $options['return']);
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exist.", array('%field' => $field, '%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exist.", array('%table' => $table, '%name' => $field)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $field, '%name_new' => $field_new)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exist.", array('%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/sqlite/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/sqlite/database.inc:      throw new DatabaseTransactionNameNonUniqueException($name . " is already in use.");
includes/database/sqlite/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/sqlite/database.inc:          throw new DatabaseTransactionCommitFailedException();
includes/database/schema.inc:      throw new DatabaseSchemaObjectExistsException(t('Table %name already exists.', array('%name' => $name)));
includes/database/database.inc:          throw new PDOException('Invalid return directive: ' . $options['return']);
includes/database/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/database.inc:      throw new DatabaseTransactionNameNonUniqueException($name . " is already in use.");
includes/database/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/database.inc:          throw new DatabaseTransactionCommitFailedException();
includes/database/database.inc:    throw new DatabaseTransactionExplicitCommitNotAllowedException();
includes/database/database.inc:      throw new DatabaseConnectionNotDefinedException('The specified database connection is not defined: ' . $key);
includes/database/database.inc:      throw new DatabaseDriverNotSpecifiedException('Driver not specified for this database connection: ' . $key);
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot copy %source to %destination.', NULL, array('%source' => $source, '%destination' => $destination));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot create directory %directory.', NULL, array('%directory' => $directory));
includes/filetransfer/local.inc:      throw new FileTransferException('removeDirectoryJailed() called with a path (%directory) that is not a directory.', NULL, array('%directory' => $directory));
includes/filetransfer/local.inc:          throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $filename));
includes/filetransfer/local.inc:          throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $filename));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $directory));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $file));
includes/filetransfer/local.inc:          throw new FileTransferException('Cannot chmod %path.', NULL, array('%path' => $filename));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot chmod %path.', NULL, array('%path' => $path));
includes/filetransfer/ssh.inc:      throw new FileTransferException('SSH Connection failed to @host:@port', NULL, array('@host' => $this->hostname, '@port' => 21));
includes/filetransfer/ssh.inc:      throw new FileTransferException('The supplied username/password combination was not accepted.');
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot copy @source_file to @destination_file.', NULL, array('@source' => $source, '@destination' => $destination));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot copy directory @directory.', NULL, array('@directory' => $source));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot create directory @directory.', NULL, array('@directory' => $directory));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $directory));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $destination));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot check @path.', NULL, array('@path' => $path));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot check @path.', NULL, array('@path' => $path));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot change permissions of @path.', NULL, array('@path' => $path));
includes/filetransfer/ftp.inc:      throw new FileTransferException('No FTP backend available.');
includes/filetransfer/ftp.inc:      throw new FileTransferException('FTP Connection failed.');
includes/filetransfer/ftp.inc:      throw new FileTransferException('Cannot copy @source_file to @destination_file.', NULL, array('@source' => $source, '@destination' => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException('Cannot remove @destination', NULL, array('@destination' => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot connect to FTP Server, check settings");
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot log in to FTP server. Check username and password");
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot move @source to @destination", NULL, array("@source" => $source, "@destination" => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot create directory @directory", NULL, array("@directory" => $directory));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to change to directory @directory", NULL, array('@directory' => $directory));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to remove to directory @directory", NULL, array('@directory' => $directory));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to remove to file @file", NULL, array('@file' => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to set permissions on %file", NULL, array ('%file' => $path));
includes/filetransfer/filetransfer.inc:    throw new FileTransferException('FileTransfer::factory() static method not overridden by FileTransfer subclass.');
includes/filetransfer/filetransfer.inc:      throw new FileTransferException('Unable to change file permissions');
includes/filetransfer/filetransfer.inc:      throw new FileTransferException('@directory is outside of the @jail', NULL, array('@directory' => $path, '@jail' => $this->jail));
includes/mail.inc:      throw new Exception(t('Class %class does not implement interface %interface', array('%class' => $class, '%interface' => 'MailSystemInterface')));
includes/install.inc:          throw new DatabaseTaskException(st("Failed to run all tasks against the database server. The task %task wasn't found.", array('%task' => $task['function'])));
includes/install.inc:      throw new DatabaseTaskException($message);
includes/install.inc:      throw new Exception(st('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
includes/install.inc:    throw new Exception(st('Failed to open %settings. Verify the file permissions.', array('%settings' => $default_settings)));
includes/install.inc:    throw new Exception(install_no_profile_error());
includes/install.inc:    throw new Exception(install_no_profile_error());
includes/updater.inc:      throw new UpdaterException(t('Unable to determine the type of the source directory.'));
includes/updater.inc:    throw new UpdaterException(t('Cannot determine the type of project.'));
includes/updater.inc:      throw new UpdaterException(t('Unable to parse info file.'));
includes/updater.inc:        throw new UpdaterException(t('Fatal error in update, cowardly refusing to wipe out the install directory.'));
includes/updater.inc:      throw new UpdaterFileTransferException(t('File Transfer failed, reason: !reason', array('!reason' => strtr($e->getMessage(), $e->arguments))));
includes/updater.inc:      throw new UpdaterFileTransferException(t('File Transfer failed, reason: !reason', array('!reason' => strtr($e->getMessage(), $e->arguments))));
includes/updater.inc:            throw new UpdaterException($throw_message);
includes/update.inc: * throw new DrupalUpdateException(t('Description of what went wrong'));
includes/authorize.inc:        throw new Exception(t('Error, this type of connection protocol (%backend) does not exist.', array('%backend' => $backend)));
includes/entity.inc:      throw new EntityFieldQueryException(t('Property query conditions were not handled in !function.', array('!function' => $function)));
includes/entity.inc:      throw new EntityFieldQueryException(t('Property query order by was not handled in !function.', array('!function' => $function)));
includes/entity.inc:        throw new EntityFieldQueryException(t("Can't handle more than one field storage engine"));
includes/entity.inc:      throw new EntityFieldQueryException(t("Field storage engine not found."));
includes/entity.inc:      throw new EntityFieldQueryException(t('For this query an entity type must be specified.'));
includes/entity.inc:      throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
includes/entity.inc:        throw new EntityFieldQueryException(t('Do not know how to order on @key for @entity_type', array('@key' => $key, '@entity_type' => $entity_type)));
includes/common.inc:    throw new Exception(t('Archivers can only operate on local files: %file not supported', array('%file' => $file)));

Some are translated, some are not, one (FileTransferException) defines a pattern of how to pass arguments to the exception without translating it.

The problem is quite simple. If we want to log exceptions (and I'm pretty sure we do!) we must not translate those strings because then we log the translated string and not the original one.

The solution is not so simple. As stated above, FileTransferException uses "public $arguments" (hi chx! ;)) to store translation placeholders. While that works and is better than throw new SomeException(t('...')), it's not so nice, because it's the only exception class that does this and we can't nicely verify if it exists (sure, isset($e->arguments) works, but I don't consider that nice :))

So...
- As #522746: Drupal Exception Wrapper Class has shown, it's too late to add a full-blown exception framework/a global exception base class to D7
- Many exceptions that don't have placeholders work just fine right now. We don't need to touch them unless we have to.
- We also need to think of php exceptions which are thrown/re-thrown.

My suggestion would be to provide at least an interface (DrupalTranslatableExceptionInterface) and probably an implementation of that (DrupalTranslatableException). That interface defines getArguments() or something similiar. We can then test if we need to get the arguments (if ($e instanceof DrupalTranslatableExceptionInterface)) in places like watchdog_exception() and drupal_log_error(). Then we simply remove t() from those exceptions that don't have any arguments and change the others to provide their arguments as necessary.

That's still a quite drastical change for being (hopefully) short before beta 1 but I don't see a better way right now.* And this could even be critical imho, since we really need to get rid of new Exception(t('...')) imho. This is just as wrong as watchdog(t('...')).

Hint: Please suggest better class/interface names but avoid bikeshedding if possible :).

* Not adding an interface and simply test $e->arguments is exactly the same in the end, an api change. Not based on clear definitions however but loose naming conventions.

PS: @chx: Just because we started using exceptions without guidelines and proper support doesn't mean that exceptions are bad, it just means that we started using them without guidelines and proper support :)

CommentFileSizeAuthor
#2 exceptions.txt24.57 KBBerdir
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Berdir’s picture

Hu?

This should have the the initial description....

----------------
(for reference, I'm aware of #522746: Drupal Exception Wrapper Class but that issue was postponed to D8 while I don't think this can be).

I did a short grep to check how we are passing the exception message to thrown exceptions because of #829484-12: Uncaught PDO Exception - XSS to find out if they always end with a point.

What I found is a translation mess ;)

$ grep -R  "throw new " modules/
modules/system/system.api.php:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.archiver.inc:      throw new Exception(t('Cannot open %file_path', array('%file_path' => $file_path)));
modules/system/system.api.php.orig:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/trigger/trigger.admin.inc:        throw new Exception(t('Missing/undefined save action (%save_aid) for %aid action.', array('%save_aid' => $aid, '%aid' => $aid)));
modules/field/modules/field_sql_storage/field_sql_storage.module:    throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
modules/field/modules/field_sql_storage/field_sql_storage.module:      throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
modules/field/tests/field_test.field.inc:    throw new FieldException("field_test 'unchangeable' setting cannot be changed'");
modules/field/tests/field_test.storage.inc:    throw new Exception('field_test_storage_failure engine always fails to create fields');
modules/field/field.attach.inc:    throw new FieldValidationException($errors);
modules/field/field.api.php:      throw new FieldUpdateForbiddenException("Cannot update a list field not to include keys with existing data");
modules/field/field.crud.inc:    throw new FieldException('Attempt to create an unnamed field.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with no type.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with a name longer than 32 characters: %name',
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:      throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field of unknown type %type.', array('%type' => $field['type'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with unknown storage type %type.', array('%type' => $field['storage']['type'])));
modules/field/field.crud.inc:    throw new FieldException('Attempt to update a non-existent field.');
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's type.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's entity_types property.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's storage type.");
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to create an instance of a field @field_name that doesn't exist or is currently inactive.", array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without an entity type.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name on forbidden entity type @entity_type.', array('@field_name' => $instance['field_name'], '@entity_type' => $instance['entity_type'])));
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to update an instance of a nonexistent field @field.', array('@field' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to update an instance of field @field on bundle @bundle that doesn't exist.", array('@field' => $instance['field_name'], '@bundle' => $instance['bundle'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to purge a field @field_name that still has instances.', array('@field_name' => $field['field_name'])));
modules/update/update.manager.inc:    throw new Exception(t('Cannot extract %file, not a valid archive.', array ('%file' => $file)));
modules/update/update.manager.inc:    throw new Exception(t('Unable to extract %file', array('%file' => $archive_file)));
modules/simpletest/tests/error_test.module:  throw new Exception("Drupal is awesome");
modules/simpletest/tests/system_test.module:  throw new Exception('Drupal is <blink>awesome</blink>.');
modules/simpletest/tests/filetransfer.test:        throw new Exception('Error removing fake module directory.');
modules/simpletest/tests/filetransfer.test:      throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
modules/node/tests/node_test_exception.module:    throw new Exception('Test exception for rollback.');
berdir@ubuntu-thinkpad:~/Projekte/d7/drupal$ grep -R  "throw new " modules/ includes/
modules/system/system.api.php:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.archiver.inc:      throw new Exception(t('Cannot open %file_path', array('%file_path' => $file_path)));
modules/system/system.api.php.orig:  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/system/system.tar.inc:        throw new Exception($p_message);
modules/trigger/trigger.admin.inc:        throw new Exception(t('Missing/undefined save action (%save_aid) for %aid action.', array('%save_aid' => $aid, '%aid' => $aid)));
modules/field/modules/field_sql_storage/field_sql_storage.module:    throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
modules/field/modules/field_sql_storage/field_sql_storage.module:      throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
modules/field/tests/field_test.field.inc:    throw new FieldException("field_test 'unchangeable' setting cannot be changed'");
modules/field/tests/field_test.storage.inc:    throw new Exception('field_test_storage_failure engine always fails to create fields');
modules/field/field.attach.inc:    throw new FieldValidationException($errors);
modules/field/field.api.php:      throw new FieldUpdateForbiddenException("Cannot update a list field not to include keys with existing data");
modules/field/field.crud.inc:    throw new FieldException('Attempt to create an unnamed field.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with no type.');
modules/field/field.crud.inc:    throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with a name longer than 32 characters: %name',
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:      throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field of unknown type %type.', array('%type' => $field['type'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create a field with unknown storage type %type.', array('%type' => $field['storage']['type'])));
modules/field/field.crud.inc:    throw new FieldException('Attempt to update a non-existent field.');
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's type.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's entity_types property.");
modules/field/field.crud.inc:    throw new FieldException("Cannot change an existing field's storage type.");
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to create an instance of a field @field_name that doesn't exist or is currently inactive.", array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without an entity type.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to create an instance of field @field_name on forbidden entity type @entity_type.', array('@field_name' => $instance['field_name'], '@entity_type' => $instance['entity_type'])));
modules/field/field.crud.inc:    throw new FieldException($message);
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to update an instance of a nonexistent field @field.', array('@field' => $instance['field_name'])));
modules/field/field.crud.inc:    throw new FieldException(t("Attempt to update an instance of field @field on bundle @bundle that doesn't exist.", array('@field' => $instance['field_name'], '@bundle' => $instance['bundle'])));
modules/field/field.crud.inc:    throw new FieldException(t('Attempt to purge a field @field_name that still has instances.', array('@field_name' => $field['field_name'])));
modules/update/update.manager.inc:    throw new Exception(t('Cannot extract %file, not a valid archive.', array ('%file' => $file)));
modules/update/update.manager.inc:    throw new Exception(t('Unable to extract %file', array('%file' => $archive_file)));
modules/simpletest/tests/error_test.module:  throw new Exception("Drupal is awesome");
modules/simpletest/tests/system_test.module:  throw new Exception('Drupal is <blink>awesome</blink>.');
modules/simpletest/tests/filetransfer.test:        throw new Exception('Error removing fake module directory.');
modules/simpletest/tests/filetransfer.test:      throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
modules/node/tests/node_test_exception.module:    throw new Exception('Test exception for rollback.');
includes/theme.inc:    throw new Exception(t('theme() may not be called until all modules are loaded.'));
includes/install.core.inc:      throw new Exception(install_already_done_error());
includes/install.core.inc:        throw new Exception(implode("\n", $errors));
includes/install.core.inc:      throw new Exception(implode("\n\n", $failures));
includes/install.core.inc:      throw new Exception(install_already_done_error());
includes/install.core.inc:        throw new Exception(install_no_profile_error());
includes/install.core.inc:    throw new Exception(install_no_profile_error());
includes/install.core.inc:        throw new Exception(st('Sorry, you must select a language to continue the installation.'));
includes/install.core.inc:    throw new Exception(st('Sorry, the profile you have chosen cannot be loaded.'));
includes/install.core.inc:    throw new Exception(install_already_done_error());
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exist.", array('%field' => $field, '%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exist.", array('%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exist.", array('%table' => $table, '%name' => $field)));
includes/database/mysql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $field, '%name_new' => $field_new)));
includes/database/query.inc:      throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
includes/database/query.inc:      throw new NoFieldsException('There are no fields available to insert with.');
includes/database/query.inc:      throw new InvalidMergeQueryException("You need to specify key fields before executing a merge query");
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exist.", array('%field' => $field, '%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exist.", array('%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exist.", array('%table' => $table, '%name' => $field)));
includes/database/pgsql/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $field, '%name_new' => $field_new)));
includes/database/pgsql/database.inc:          throw new PDOException('Invalid return directive: ' . $options['return']);
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exist.", array('%field' => $field, '%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exist.", array('%table' => $table, '%name' => $field)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $field, '%name_new' => $field_new)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exist.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exist.", array('%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/sqlite/schema.inc:      throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exist.", array('%table' => $table, '%field' => $field)));
includes/database/sqlite/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/sqlite/database.inc:      throw new DatabaseTransactionNameNonUniqueException($name . " is already in use.");
includes/database/sqlite/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/sqlite/database.inc:          throw new DatabaseTransactionCommitFailedException();
includes/database/schema.inc:      throw new DatabaseSchemaObjectExistsException(t('Table %name already exists.', array('%name' => $name)));
includes/database/database.inc:          throw new PDOException('Invalid return directive: ' . $options['return']);
includes/database/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/database.inc:      throw new DatabaseTransactionNameNonUniqueException($name . " is already in use.");
includes/database/database.inc:      throw new DatabaseTransactionNoActiveException();
includes/database/database.inc:          throw new DatabaseTransactionCommitFailedException();
includes/database/database.inc:    throw new DatabaseTransactionExplicitCommitNotAllowedException();
includes/database/database.inc:      throw new DatabaseConnectionNotDefinedException('The specified database connection is not defined: ' . $key);
includes/database/database.inc:      throw new DatabaseDriverNotSpecifiedException('Driver not specified for this database connection: ' . $key);
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot copy %source to %destination.', NULL, array('%source' => $source, '%destination' => $destination));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot create directory %directory.', NULL, array('%directory' => $directory));
includes/filetransfer/local.inc:      throw new FileTransferException('removeDirectoryJailed() called with a path (%directory) that is not a directory.', NULL, array('%directory' => $directory));
includes/filetransfer/local.inc:          throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $filename));
includes/filetransfer/local.inc:          throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $filename));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $directory));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $file));
includes/filetransfer/local.inc:          throw new FileTransferException('Cannot chmod %path.', NULL, array('%path' => $filename));
includes/filetransfer/local.inc:      throw new FileTransferException('Cannot chmod %path.', NULL, array('%path' => $path));
includes/filetransfer/ssh.inc:      throw new FileTransferException('SSH Connection failed to @host:@port', NULL, array('@host' => $this->hostname, '@port' => 21));
includes/filetransfer/ssh.inc:      throw new FileTransferException('The supplied username/password combination was not accepted.');
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot copy @source_file to @destination_file.', NULL, array('@source' => $source, '@destination' => $destination));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot copy directory @directory.', NULL, array('@directory' => $source));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot create directory @directory.', NULL, array('@directory' => $directory));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $directory));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $destination));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot check @path.', NULL, array('@path' => $path));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot check @path.', NULL, array('@path' => $path));
includes/filetransfer/ssh.inc:      throw new FileTransferException('Cannot change permissions of @path.', NULL, array('@path' => $path));
includes/filetransfer/ftp.inc:      throw new FileTransferException('No FTP backend available.');
includes/filetransfer/ftp.inc:      throw new FileTransferException('FTP Connection failed.');
includes/filetransfer/ftp.inc:      throw new FileTransferException('Cannot copy @source_file to @destination_file.', NULL, array('@source' => $source, '@destination' => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException('Cannot remove @destination', NULL, array('@destination' => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot connect to FTP Server, check settings");
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot log in to FTP server. Check username and password");
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot move @source to @destination", NULL, array("@source" => $source, "@destination" => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Cannot create directory @directory", NULL, array("@directory" => $directory));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to change to directory @directory", NULL, array('@directory' => $directory));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to remove to directory @directory", NULL, array('@directory' => $directory));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to remove to file @file", NULL, array('@file' => $destination));
includes/filetransfer/ftp.inc:      throw new FileTransferException("Unable to set permissions on %file", NULL, array ('%file' => $path));
includes/filetransfer/filetransfer.inc:    throw new FileTransferException('FileTransfer::factory() static method not overridden by FileTransfer subclass.');
includes/filetransfer/filetransfer.inc:      throw new FileTransferException('Unable to change file permissions');
includes/filetransfer/filetransfer.inc:      throw new FileTransferException('@directory is outside of the @jail', NULL, array('@directory' => $path, '@jail' => $this->jail));
includes/mail.inc:      throw new Exception(t('Class %class does not implement interface %interface', array('%class' => $class, '%interface' => 'MailSystemInterface')));
includes/install.inc:          throw new DatabaseTaskException(st("Failed to run all tasks against the database server. The task %task wasn't found.", array('%task' => $task['function'])));
includes/install.inc:      throw new DatabaseTaskException($message);
includes/install.inc:      throw new Exception(st('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
includes/install.inc:    throw new Exception(st('Failed to open %settings. Verify the file permissions.', array('%settings' => $default_settings)));
includes/install.inc:    throw new Exception(install_no_profile_error());
includes/install.inc:    throw new Exception(install_no_profile_error());
includes/updater.inc:      throw new UpdaterException(t('Unable to determine the type of the source directory.'));
includes/updater.inc:    throw new UpdaterException(t('Cannot determine the type of project.'));
includes/updater.inc:      throw new UpdaterException(t('Unable to parse info file.'));
includes/updater.inc:        throw new UpdaterException(t('Fatal error in update, cowardly refusing to wipe out the install directory.'));
includes/updater.inc:      throw new UpdaterFileTransferException(t('File Transfer failed, reason: !reason', array('!reason' => strtr($e->getMessage(), $e->arguments))));
includes/updater.inc:      throw new UpdaterFileTransferException(t('File Transfer failed, reason: !reason', array('!reason' => strtr($e->getMessage(), $e->arguments))));
includes/updater.inc:            throw new UpdaterException($throw_message);
includes/update.inc: * throw new DrupalUpdateException(t('Description of what went wrong'));
includes/authorize.inc:        throw new Exception(t('Error, this type of connection protocol (%backend) does not exist.', array('%backend' => $backend)));
includes/entity.inc:      throw new EntityFieldQueryException(t('Property query conditions were not handled in !function.', array('!function' => $function)));
includes/entity.inc:      throw new EntityFieldQueryException(t('Property query order by was not handled in !function.', array('!function' => $function)));
includes/entity.inc:        throw new EntityFieldQueryException(t("Can't handle more than one field storage engine"));
includes/entity.inc:      throw new EntityFieldQueryException(t("Field storage engine not found."));
includes/entity.inc:      throw new EntityFieldQueryException(t('For this query an entity type must be specified.'));
includes/entity.inc:      throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
includes/entity.inc:        throw new EntityFieldQueryException(t('Do not know how to order on @key for @entity_type', array('@key' => $key, '@entity_type' => $entity_type)));
includes/common.inc:    throw new Exception(t('Archivers can only operate on local files: %file not supported', array('%file' => $file)));

Some are translated, some are not, one (FileTransferException) defines a pattern of how to pass arguments to the exception without translating it.

The problem is quite simple. If we want to log exceptions (and I'm pretty sure we do!) we must not translate those strings because then we log the translated string and not the original one.

The solution is not so simple. As stated above, FileTransferException uses "public $arguments" (hi chx! ;)) to store translation placeholders. While that works and is better than throw new SomeException(t('...')), it's not so nice, because it's the only exception class that does this and we can't nicely verify if it exists (sure, isset($e->arguments) works, but I don't consider that nice :))

So...
- As #522746: Drupal Exception Wrapper Class has shown, it's too late to add a full-blown exception framework/a global exception base class to D7
- Many exceptions that don't have placeholders work just fine right now. We don't need to touch them unless we have to.
- We also need to think of php exceptions which are thrown/re-thrown.

My suggestion would be to provide at least an interface (DrupalTranslatableExceptionInterface) and probably an implementation of that (DrupalTranslatableException). That interface defines getArguments() or something similiar. We can then test if we need to get the arguments (if ($e instanceof DrupalTranslatableExceptionInterface)) in places like watchdog_exception() and drupal_log_error(). Then we simply remove t() from those exceptions that don't have any arguments and change the others to provide their arguments as necessary.

That's still a quite drastical change for being (hopefully) short before beta 1 but I don't see a better way right now.* And this could even be critical imho, since we really need to get rid of new Exception(t('...')) imho. This is just as wrong as watchdog(t('...')).

Hint: Please suggest better class/interface names but avoid bikeshedding if possible :).

* Not adding an interface and simply test $e->arguments is exactly the same in the end, an api change. Not based on clear definitions however but loose naming conventions.

PS: @chx: Just because we started using exceptions without guidelines and proper support doesn't mean that exceptions are bad, it just means that we started using them without guidelines and proper support :)

Berdir’s picture

FileSize
24.57 KB

Drupal is eating my posts :(

Let's try once more without the long grep list.

---- Original post without grep list

(for reference, I'm aware of #522746: Drupal Exception Wrapper Class but that issue was postponed to D8 while I don't think this can be).

I did a short grep to check how we are passing the exception message to thrown exceptions because of #829484-12: Uncaught PDO Exception - XSS to find out if they always end with a point.

What I found is a translation mess, see attached file ;) (posting with that in the text resulted in an empty descrption, see above)

Some are translated, some are not, one (FileTransferException) defines a pattern of how to pass arguments to the exception without translating it.

The problem is quite simple. If we want to log exceptions (and I'm pretty sure we do!) we must not translate those strings because then we log the translated string and not the original one.

The solution is not so simple. As stated above, FileTransferException uses "public $arguments" (hi chx! ;)) to store translation placeholders. While that works and is better than throw new SomeException(t('...')), it's not so nice, because it's the only exception class that does this and we can't nicely verify if it exists (sure, isset($e->arguments) works, but I don't consider that nice :))

So...
- As #522746: Drupal Exception Wrapper Class has shown, it's too late to add a full-blown exception framework/a global exception base class to D7
- Many exceptions that don't have placeholders work just fine right now. We don't need to touch them unless we have to.
- We also need to think of php exceptions which are thrown/re-thrown.

My suggestion would be to provide at least an interface (DrupalTranslatableExceptionInterface) and probably an implementation of that (DrupalTranslatableException). That interface defines getArguments() or something similiar. We can then test if we need to get the arguments (if ($e instanceof DrupalTranslatableExceptionInterface)) in places like watchdog_exception() and drupal_log_error(). Then we simply remove t() from those exceptions that don't have any arguments and change the others to provide their arguments as necessary.

That's still a quite drastical change for being (hopefully) short before beta 1 but I don't see a better way right now.* And this could even be critical imho, since we really need to get rid of new Exception(t('...')) imho. This is just as wrong as watchdog(t('...')).

Hint: Please suggest better class/interface names but avoid bikeshedding if possible :).

* Not adding an interface and simply test $e->arguments is exactly the same in the end, an api change. Not based on clear definitions however but loose naming conventions.

PS: @chx: Just because we started using exceptions without guidelines and proper support doesn't mean that exceptions are bad, it just means that we started using them without guidelines and proper support :)

Berdir’s picture

http://drupal.org/node/608166 needs to be updated if this results in a commited patch/agreement :)

Berdir’s picture

Status: Active » Closed (duplicate)

Closing as a duplicate of #522746: Drupal Exception Wrapper Class, let's see if we can revive that one.