以Kohana Cache设计为例

1、抽象类:E:\html\tproject\framework\modules\cache\classes\Cache.php

<?php defined('SYSPATH') or die('No direct script access.');

abstract class Cache extends Kohana_Cache {}

2、抽象类:E:\html\tproject\framework\modules\cache\classes\Kohana\Cache.php

<?php defined('SYSPATH') or die('No direct script access.');
/**
* Kohana Cache provides a common interface to a variety of caching engines. Tags are
* supported where available natively to the cache system. Kohana Cache supports multiple
* instances of cache engines through a grouped singleton pattern.
*
* ### Supported cache engines
*
* * [APC](http://php.net/manual/en/book.apc.php)
* * [eAccelerator](http://eaccelerator.net/)
* * File
* * [Memcache](http://memcached.org/)
* * [Memcached-tags](http://code.google.com/p/memcached-tags/)
* * [SQLite](http://www.sqlite.org/)
* * [Xcache](http://xcache.lighttpd.net/)
*
* ### Introduction to caching
*
* Caching should be implemented with consideration. Generally, caching the result of resources
* is faster than reprocessing them. Choosing what, how and when to cache is vital. PHP APC is
* presently one of the fastest caching systems available, closely followed by Memcache. SQLite
* and File caching are two of the slowest cache methods, however usually faster than reprocessing
* a complex set of instructions.
*
* Caching engines that use memory are considerably faster than the file based alternatives. But
* memory is limited whereas disk space is plentiful. If caching large datasets it is best to use
* file caching.
*
* ### Configuration settings
*
* Kohana Cache uses configuration groups to create cache instances. A configuration group can
* use any supported driver, with successive groups using the same driver type if required.
*
* #### Configuration example
*
* Below is an example of a _memcache_ server configuration.
*
* return array(
* 'memcache' => array( // Name of group
* 'driver' => 'memcache', // using Memcache driver
* 'servers' => array( // Available server definitions
* array(
* 'host' => 'localhost',
* 'port' => 11211,
* 'persistent' => FALSE
* )
* ),
* 'compression' => FALSE, // Use compression?
* ),
* )
*
* In cases where only one cache group is required, set `Cache::$default` (in your bootstrap,
* or by extending `Kohana_Cache` class) to the name of the group, and use:
*
* $cache = Cache::instance(); // instead of Cache::instance('memcache')
*
* It will return the cache instance of the group it has been set in `Cache::$default`.
*
* #### General cache group configuration settings
*
* Below are the settings available to all types of cache driver.
*
* Name | Required | Description
* -------------- | -------- | ---------------------------------------------------------------
* driver | __YES__ | (_string_) The driver type to use
*
* Details of the settings specific to each driver are available within the drivers documentation.
*
* ### System requirements
*
* * Kohana 3.0.x
* * PHP 5.2.4 or greater
*
* @package Kohana/Cache
* @category Base
* @version 2.0
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
abstract class Kohana_Cache { const DEFAULT_EXPIRE = 3600; /**
* @var string default driver to use
*/
public static $default = 'file'; /**
* @var Kohana_Cache instances
*/
public static $instances = array(); /**
* Creates a singleton of a Kohana Cache group. If no group is supplied
* the __default__ cache group is used.
*
* // Create an instance of the default group
* $default_group = Cache::instance();
*
* // Create an instance of a group
* $foo_group = Cache::instance('foo');
*
* // Access an instantiated group directly
* $foo_group = Cache::$instances['default'];
*
* @param string $group the name of the cache group to use [Optional]
* @return Cache
* @throws Cache_Exception
*/
public static function instance($group = NULL)
{
// If there is no group supplied
if ($group === NULL)
{
// Use the default setting
$group = Cache::$default;
} if (isset(Cache::$instances[$group]))
{
// Return the current group if initiated already
return Cache::$instances[$group];
} $config = Kohana::$config->load('cache'); if ( ! $config->offsetExists($group))
{
throw new Cache_Exception(
'Failed to load Kohana Cache group: :group',
array(':group' => $group)
);
} $config = $config->get($group); // Create a new cache type instance
$cache_class = 'Cache_'.ucfirst($config['driver']);
Cache::$instances[$group] = new $cache_class($config); // Return the instance
return Cache::$instances[$group];
} /**
* @var Config
*/
protected $_config = array(); /**
* Ensures singleton pattern is observed, loads the default expiry
*
* @param array $config configuration
*/
protected function __construct(array $config)
{
$this->config($config);
} /**
* Getter and setter for the configuration. If no argument provided, the
* current configuration is returned. Otherwise the configuration is set
* to this class.
*
* // Overwrite all configuration
* $cache->config(array('driver' => 'memcache', '...'));
*
* // Set a new configuration setting
* $cache->config('servers', array(
* 'foo' => 'bar',
* '...'
* ));
*
* // Get a configuration setting
* $servers = $cache->config('servers);
*
* @param mixed key to set to array, either array or config path
* @param mixed value to associate with key
* @return mixed
*/
public function config($key = NULL, $value = NULL)
{
if ($key === NULL)
return $this->_config; if (is_array($key))
{
$this->_config = $key;
}
else
{
if ($value === NULL)
return Arr::get($this->_config, $key); $this->_config[$key] = $value;
} return $this;
} /**
* Overload the __clone() method to prevent cloning
*
* @return void
* @throws Cache_Exception
*/
final public function __clone()
{
throw new Cache_Exception('Cloning of Kohana_Cache objects is forbidden');
} /**
* Retrieve a cached value entry by id.
*
* // Retrieve cache entry from default group
* $data = Cache::instance()->get('foo');
*
* // Retrieve cache entry from default group and return 'bar' if miss
* $data = Cache::instance()->get('foo', 'bar');
*
* // Retrieve cache entry from memcache group
* $data = Cache::instance('memcache')->get('foo');
*
* @param string $id id of cache to entry
* @param string $default default value to return if cache miss
* @return mixed
* @throws Cache_Exception
*/
abstract public function get($id, $default = NULL); /**
* Set a value to cache with id and lifetime
*
* $data = 'bar';
*
* // Set 'bar' to 'foo' in default group, using default expiry
* Cache::instance()->set('foo', $data);
*
* // Set 'bar' to 'foo' in default group for 30 seconds
* Cache::instance()->set('foo', $data, 30);
*
* // Set 'bar' to 'foo' in memcache group for 10 minutes
* if (Cache::instance('memcache')->set('foo', $data, 600))
* {
* // Cache was set successfully
* return
* }
*
* @param string $id id of cache entry
* @param string $data data to set to cache
* @param integer $lifetime lifetime in seconds
* @return boolean
*/
abstract public function set($id, $data, $lifetime = 3600); /**
* Delete a cache entry based on id
*
* // Delete 'foo' entry from the default group
* Cache::instance()->delete('foo');
*
* // Delete 'foo' entry from the memcache group
* Cache::instance('memcache')->delete('foo')
*
* @param string $id id to remove from cache
* @return boolean
*/
abstract public function delete($id); /**
* Delete all cache entries.
*
* Beware of using this method when
* using shared memory cache systems, as it will wipe every
* entry within the system for all clients.
*
* // Delete all cache entries in the default group
* Cache::instance()->delete_all();
*
* // Delete all cache entries in the memcache group
* Cache::instance('memcache')->delete_all();
*
* @return boolean
*/
abstract public function delete_all(); /**
* Replaces troublesome characters with underscores.
*
* // Sanitize a cache id
* $id = $this->_sanitize_id($id);
*
* @param string $id id of cache to sanitize
* @return string
*/
protected function _sanitize_id($id)
{
// Change slashes and spaces to underscores
return str_replace(array('/', '\\', ' '), '_', $id);
}
}
// End Kohana_Cache

 

3、缓存方式File、Memcache、Apc、Wincache实现类

3.1、E:\html\tproject\framework\modules\cache\classes\Cache\File.php

<?php defined('SYSPATH') or die('No direct script access.');

class Cache_File extends Kohana_Cache_File {}

3.2、E:\html\tproject\framework\modules\cache\classes\Kohana\Cache\File.php

<?php defined('SYSPATH') or die('No direct script access.');
/**
* [Kohana Cache](api/Kohana_Cache) File driver. Provides a file based
* driver for the Kohana Cache library. This is one of the slowest
* caching methods.
*
* ### Configuration example
*
* Below is an example of a _file_ server configuration.
*
* return array(
* 'file' => array( // File driver group
* 'driver' => 'file', // using File driver
* 'cache_dir' => APPPATH.'cache/.kohana_cache', // Cache location
* ),
* )
*
* In cases where only one cache group is required, if the group is named `default` there is
* no need to pass the group name when instantiating a cache instance.
*
* #### General cache group configuration settings
*
* Below are the settings available to all types of cache driver.
*
* Name | Required | Description
* -------------- | -------- | ---------------------------------------------------------------
* driver | __YES__ | (_string_) The driver type to use
* cache_dir | __NO__ | (_string_) The cache directory to use for this cache instance
*
* ### System requirements
*
* * Kohana 3.0.x
* * PHP 5.2.4 or greater
*
* @package Kohana/Cache
* @category Base
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Cache_File extends Cache implements Cache_GarbageCollect { /**
* Creates a hashed filename based on the string. This is used
* to create shorter unique IDs for each cache filename.
*
* // Create the cache filename
* $filename = Cache_File::filename($this->_sanitize_id($id));
*
* @param string $string string to hash into filename
* @return string
*/
protected static function filename($string)
{
return sha1($string).'.cache';
} /**
* @var string the caching directory
*/
protected $_cache_dir; /**
* Constructs the file cache driver. This method cannot be invoked externally. The file cache driver must
* be instantiated using the `Cache::instance()` method.
*
* @param array $config config
* @throws Cache_Exception
*/
protected function __construct(array $config)
{
// Setup parent
parent::__construct($config); try
{
$directory = Arr::get($this->_config, 'cache_dir', Kohana::$cache_dir);
$this->_cache_dir = new SplFileInfo($directory);
}
// PHP < 5.3 exception handle
catch (ErrorException $e)
{
$this->_cache_dir = $this->_make_directory($directory, 0777, TRUE);
}
// PHP >= 5.3 exception handle
catch (UnexpectedValueException $e)
{
$this->_cache_dir = $this->_make_directory($directory, 0777, TRUE);
} // If the defined directory is a file, get outta here
if ($this->_cache_dir->isFile())
{
throw new Cache_Exception('Unable to create cache directory as a file already exists : :resource', array(':resource' => $this->_cache_dir->getRealPath()));
} // Check the read status of the directory
if ( ! $this->_cache_dir->isReadable())
{
throw new Cache_Exception('Unable to read from the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
} // Check the write status of the directory
if ( ! $this->_cache_dir->isWritable())
{
throw new Cache_Exception('Unable to write to the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
}
} /**
* Retrieve a cached value entry by id.
*
* // Retrieve cache entry from file group
* $data = Cache::instance('file')->get('foo');
*
* // Retrieve cache entry from file group and return 'bar' if miss
* $data = Cache::instance('file')->get('foo', 'bar');
*
* @param string $id id of cache to entry
* @param string $default default value to return if cache miss
* @return mixed
* @throws Cache_Exception
*/
public function get($id, $default = NULL)
{
$filename = Cache_File::filename($this->_sanitize_id($id));
$directory = $this->_resolve_directory($filename); // Wrap operations in try/catch to handle notices
try
{
// Open file
$file = new SplFileInfo($directory.$filename); // If file does not exist
if ( ! $file->isFile())
{
// Return default value
return $default;
}
else
{
// Test the expiry
if ($this->_is_expired($file))
{
// Delete the file
$this->_delete_file($file, FALSE, TRUE);
return $default;
} // open the file to read data
$data = $file->openFile(); // Run first fgets(). Cache data starts from the second line
// as the first contains the lifetime timestamp
$data->fgets(); $cache = ''; while ($data->eof() === FALSE)
{
$cache .= $data->fgets();
} return unserialize($cache);
} }
catch (ErrorException $e)
{
// Handle ErrorException caused by failed unserialization
if ($e->getCode() === E_NOTICE)
{
throw new Cache_Exception(__METHOD__.' failed to unserialize cached object with message : '.$e->getMessage());
} // Otherwise throw the exception
throw $e;
}
} /**
* Set a value to cache with id and lifetime
*
* $data = 'bar';
*
* // Set 'bar' to 'foo' in file group, using default expiry
* Cache::instance('file')->set('foo', $data);
*
* // Set 'bar' to 'foo' in file group for 30 seconds
* Cache::instance('file')->set('foo', $data, 30);
*
* @param string $id id of cache entry
* @param string $data data to set to cache
* @param integer $lifetime lifetime in seconds
* @return boolean
*/
public function set($id, $data, $lifetime = NULL)
{
$filename = Cache_File::filename($this->_sanitize_id($id));
$directory = $this->_resolve_directory($filename); // If lifetime is NULL
if ($lifetime === NULL)
{
// Set to the default expiry
$lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
} // Open directory
$dir = new SplFileInfo($directory); // If the directory path is not a directory
if ( ! $dir->isDir())
{
$this->_make_directory($directory, 0777, TRUE);
} // Open file to inspect
$resouce = new SplFileInfo($directory.$filename);
$file = $resouce->openFile('w'); try
{
$data = $lifetime."\n".serialize($data);
$file->fwrite($data, strlen($data));
return (bool) $file->fflush();
}
catch (ErrorException $e)
{
// If serialize through an error exception
if ($e->getCode() === E_NOTICE)
{
// Throw a caching error
throw new Cache_Exception(__METHOD__.' failed to serialize data for caching with message : '.$e->getMessage());
} // Else rethrow the error exception
throw $e;
}
} /**
* Delete a cache entry based on id
*
* // Delete 'foo' entry from the file group
* Cache::instance('file')->delete('foo');
*
* @param string $id id to remove from cache
* @return boolean
*/
public function delete($id)
{
$filename = Cache_File::filename($this->_sanitize_id($id));
$directory = $this->_resolve_directory($filename); return $this->_delete_file(new SplFileInfo($directory.$filename), FALSE, TRUE);
} /**
* Delete all cache entries.
*
* Beware of using this method when
* using shared memory cache systems, as it will wipe every
* entry within the system for all clients.
*
* // Delete all cache entries in the file group
* Cache::instance('file')->delete_all();
*
* @return boolean
*/
public function delete_all()
{
return $this->_delete_file($this->_cache_dir, TRUE);
} /**
* Garbage collection method that cleans any expired
* cache entries from the cache.
*
* @return void
*/
public function garbage_collect()
{
$this->_delete_file($this->_cache_dir, TRUE, FALSE, TRUE);
return;
} /**
* Deletes files recursively and returns FALSE on any errors
*
* // Delete a file or folder whilst retaining parent directory and ignore all errors
* $this->_delete_file($folder, TRUE, TRUE);
*
* @param SplFileInfo $file file
* @param boolean $retain_parent_directory retain the parent directory
* @param boolean $ignore_errors ignore_errors to prevent all exceptions interrupting exec
* @param boolean $only_expired only expired files
* @return boolean
* @throws Cache_Exception
*/
protected function _delete_file(SplFileInfo $file, $retain_parent_directory = FALSE, $ignore_errors = FALSE, $only_expired = FALSE)
{
// Allow graceful error handling
try
{
// If is file
if ($file->isFile())
{
try
{
// Handle ignore files
if (in_array($file->getFilename(), $this->config('ignore_on_delete')))
{
$delete = FALSE;
}
// If only expired is not set
elseif ($only_expired === FALSE)
{
// We want to delete the file
$delete = TRUE;
}
// Otherwise...
else
{
// Assess the file expiry to flag it for deletion
$delete = $this->_is_expired($file);
} // If the delete flag is set delete file
if ($delete === TRUE)
return unlink($file->getRealPath());
else
return FALSE;
}
catch (ErrorException $e)
{
// Catch any delete file warnings
if ($e->getCode() === E_WARNING)
{
throw new Cache_Exception(__METHOD__.' failed to delete file : :file', array(':file' => $file->getRealPath()));
}
}
}
// Else, is directory
elseif ($file->isDir())
{
// Create new DirectoryIterator
$files = new DirectoryIterator($file->getPathname()); // Iterate over each entry
while ($files->valid())
{
// Extract the entry name
$name = $files->getFilename(); // If the name is not a dot
if ($name != '.' AND $name != '..')
{
// Create new file resource
$fp = new SplFileInfo($files->getRealPath());
// Delete the file
$this->_delete_file($fp, $retain_parent_directory, $ignore_errors, $only_expired);
} // Move the file pointer on
$files->next();
} // If set to retain parent directory, return now
if ($retain_parent_directory)
{
return TRUE;
} try
{
// Remove the files iterator
// (fixes Windows PHP which has permission issues with open iterators)
unset($files); // Try to remove the parent directory
return rmdir($file->getRealPath());
}
catch (ErrorException $e)
{
// Catch any delete directory warnings
if ($e->getCode() === E_WARNING)
{
throw new Cache_Exception(__METHOD__.' failed to delete directory : :directory', array(':directory' => $file->getRealPath()));
}
throw $e;
}
}
else
{
// We get here if a file has already been deleted
return FALSE;
}
}
// Catch all exceptions
catch (Exception $e)
{
// If ignore_errors is on
if ($ignore_errors === TRUE)
{
// Return
return FALSE;
}
// Throw exception
throw $e;
}
} /**
* Resolves the cache directory real path from the filename
*
* // Get the realpath of the cache folder
* $realpath = $this->_resolve_directory($filename);
*
* @param string $filename filename to resolve
* @return string
*/
protected function _resolve_directory($filename)
{
return $this->_cache_dir->getRealPath().DIRECTORY_SEPARATOR.$filename[0].$filename[1].DIRECTORY_SEPARATOR;
} /**
* Makes the cache directory if it doesn't exist. Simply a wrapper for
* `mkdir` to ensure DRY principles
*
* @link http://php.net/manual/en/function.mkdir.php
* @param string $directory directory path
* @param integer $mode chmod mode
* @param boolean $recursive allows nested directories creation
* @param resource $context a stream context
* @return SplFileInfo
* @throws Cache_Exception
*/
protected function _make_directory($directory, $mode = 0777, $recursive = FALSE, $context = NULL)
{
// call mkdir according to the availability of a passed $context param
$mkdir_result = $context ?
mkdir($directory, $mode, $recursive, $context) :
mkdir($directory, $mode, $recursive); // throw an exception if unsuccessful
if ( ! $mkdir_result)
{
throw new Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory));
} // chmod to solve potential umask issues
chmod($directory, $mode); return new SplFileInfo($directory);
} /**
* Test if cache file is expired
*
* @param SplFileInfo $file the cache file
* @return boolean TRUE if expired false otherwise
*/
protected function _is_expired(SplFileInfo $file)
{
// Open the file and parse data
$created = $file->getMTime();
$data = $file->openFile("r");
$lifetime = (int) $data->fgets(); // If we're at the EOF at this point, corrupted!
if ($data->eof())
{
throw new Cache_Exception(__METHOD__ . ' corrupted cache file!');
} //close file
$data = null; // test for expiry and return
return (($lifetime !== 0) AND ( ($created + $lifetime) < time()));
}
}

  

4、使用

$key = 'AjSR/piQT24JaYEgh6V9tA==';
$data = array('id'=>1, 'name'=>'liuchao');
Cache::instance("file")->set($key, $data, 60);

  

php 抽象类适配器设计模式的更多相关文章

  1. Java适配器设计模式

    适配器设计模式,一个接口首先被一个抽象类先实现(此抽象类通常称为适配器类),并在此抽象类中实现若干方法(但是这个抽象类中的方法体是空的),则以后的子类直接继承此抽象类,就可以有选择地覆写所需要的方法. ...

  2. [gkk]传智-适配器设计模式,如同电源适配器

    //适配器设计模式 是图形化设计中用的.如同电源适配器 import java.awt.*; inport java.awte public calss MyFrame{ public static ...

  3. JAVA基础—适配器设计模式

    适配器概念 在计算机编程中,适配器模式将一个类的接口适配成用户所期待的.使用适配器,可以使接口不兼容而无法在一起工作的类协调工作,做法是将类自己包裹在一个已经存在的类中. JDK对适配器设计模式的应用 ...

  4. Java设计模式之适配器设计模式(项目升级案例)

    今天是我学习到Java设计模式中的第三个设计模式了,但是天气又开始变得狂热起来,对于我这个凉爽惯了的青藏人来说,又是非常闹心的一件事儿,好了不管怎么样,目标还是目标(争取把23种Java设计模式接触一 ...

  5. IOS设计模式第六篇之适配器设计模式

    版权声明:原创作品,谢绝转载!否则将追究法律责任. 那么怎么使用适配器设计模式呢? 这个之前提到的水平滚动的视图像这样: 为了开始实现他,我们创建一个新的继承与UIView的HorizontalScr ...

  6. 适配器设计模式初探(Java实现)

    本篇随笔主要介绍Java实现设配器设计模式. 先来看下待解决的问题: (图片转自http://blog.csdn.net/jason0539) 由上图的情况可知,欧洲壁式插座只有三足插口,如果我们想要 ...

  7. Design Pattern Adaptor 适配器设计模式

    适配器设计模式是为了要使用一个旧的接口,或许这个接口非常难用,或许是和新的更新的接口不兼容,所以须要设计一个适配器类,然后就能够让新旧的接口都统一. 就是这种一个图: watermark/2/text ...

  8. 适配器设计模式及GenericServlet(九)

    一共两个知识点:1.Servlet 里面已经有适配器了,名字叫:GenericServlet.      2.什么是适配器模式. 如果这个接口里面有好多方法,那创建A/B/C这个三个类的时候如果必须继 ...

  9. php适配器设计模式

    <?php //适配器模式 //服务器端代码 class tianqi{ public static function show(){ $today= array('tep' =>28 , ...

随机推荐

  1. 根据select的option文本选中对应的选项

    function selectByOptionTxt(obj,txt){ var optionArr = $(obj).find("option"); for(var i=0;i& ...

  2. python并发编程之进程池、线程池、协程

    需要注意一下不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去自己加 ...

  3. (二)inlineCallbacks,同步方式写异步代码

    一. 上篇提到了用defered对象注册回调的方式,来处理异步操作,这样大家都知道,实际情况代码很难搞的.因为当业务逻辑复杂后,这边一个异步操作,我们注册一个回调,代码跳到A地方,A里面也有异步操作, ...

  4. ThinkPHP5 与 composer

    我是通过安装composer来获取tp5安装的,也是学习一下 在 Windows 中,你需要下载并运行 Composer-Setup.exe. 安装时,要选择本地环境中php.exe作为跟目录,这个可 ...

  5. 数塔 Medium

    Summer is coming! It's time for Iahub and Iahubina to work out, as they both want to look hot at the ...

  6. RouteReuseStrategy angular路由复用策略详解,深度刨析路由复用策略

    关于前端路由复用策略网上的文章很多,大多是讲如何实现tab标签切换历史数据,至于如何复用的原理讲的都比较朦胧,代码样例也很难适用各种各样的路由配置,比如懒加载模式下多级嵌套路由出口网上的大部分代码都会 ...

  7. 用Java构建一个简单的WebSocket聊天室

    前言 首先对于一个简单的聊天室,大家应该都有一定的概念了,这里我们省略用户模块的讲解,而是单纯的先说说聊天室的几个功能:自我对话.好友交流.群聊.离线消息等. 今天我们要做的demo就能帮我们做到这一 ...

  8. hdu6468 zyb的面试 (思维)

    题目传送门 题意: 将1~n个数按字典序排序后,求第k个数 思路: 代码: #include<stdio.h> #include<iostream> #include<a ...

  9. 数据库管理利器——Navicat Premium v12.1.25 下载和安装

    目录 1. 按 2. 新功能 3. 安装 4. 激活 5. 下载地址 1. 按 Navicat Premium 是一套数据库管理工具,让你以单一程序同時连接到 MySQL.MariaDB.SQL Se ...

  10. 010-流程控制 while 与 until 语句

    流程控制 while 与 until 语句 while循环是不定循环,也称作条件循环,只要条件成立,循环就一直继续.与for的固定循环不同 until只要条件不成立,循环就一直继续 #!/bin/ba ...